Load libraries

library(readr)
library(dplyr)
library(here)
library(readxl)
library(stringr)
library(tidyverse)
library(sf)
library(rnaturalearth)

Read the data

db_resurv<-read_tsv(here("data", "edited", "db_resurv.csv"))
Warning: One or more parsing issues, call `problems()` on your data frame for details, e.g.:
  dat <- vroom(...)
  problems(dat)Rows: 425310 Columns: 69── Column specification ─────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (31): Country, Biblioreference, Nr. table in publ., Cover abundance scale, Author, Date o...
dbl (22): PlotObservationID, PlotID, TV2 relevé number, Nr. relevé in table, Relevé area (m²)...
lgl (16): Lon1, Lon2, Lon3, Lon4, Lat1, Lat2, Lat3, Lat4, X1, X2, X3, X4, Y1, Y2, Y3, Y4
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Problems (do not affect us)

problems<-problems(db_resurv)
sort(unique(problems$col))
[1]  7 13
names(db_resurv[c(7,13)])
[1] "Nr. relevé in table" "Altitude"           

No problems really!

Update coordinates

Create new column with old coordinates if new not available, and with new if available.

db_resurv <- db_resurv %>%
  mutate(Lon_updated = ifelse(is.na(Lon_prec), Longitude, Lon_prec),
         Lat_updated = ifelse(is.na(Lat_prec), Latitude, Lat_prec))
print(db_resurv, width = Inf)

Barplot of coordinate status

# Define a threshold (e.g., 0.001 degrees for longitude/latitude differences)
threshold <- 0.001
db_resurv %>% 
  group_by(RS_CODE, `ReSurvey site`, `ReSurvey plot`) %>%
  mutate(
    lon_range = ifelse(all(is.na(Lon_updated)), NA,
                        max(Lon_updated, na.rm = T) - 
                         min(Lon_updated, na.rm = T)),
    lat_range = ifelse(all(is.na(Lat_updated)), NA,
                        max(Lat_updated, na.rm = T) - 
                         min(Lat_updated, na.rm = T)),
    coordinates_equal = ifelse(is.na(Lon_updated) & is.na(Lat_updated), NA,
                               lon_range == 0 & lat_range == 0),
    coordinates_consistent = ifelse(is.na(Lon_updated) & is.na(Lat_updated), NA,
                                    lon_range < threshold & 
                                      lat_range < threshold)
  ) %>%
  ungroup() %>%
  group_by(RS_CODE,`ReSurvey site`, `ReSurvey plot`) %>%
  summarize(is_equal = all(coordinates_equal),
            is_consistent = all(coordinates_consistent),
            .groups = "drop") %>%
  mutate(coordinate_status = case_when(
    is_equal ~ "Equal",
    !is_equal & is_consistent ~ "Consistent (< 0.001º)",
    !is_equal & !is_consistent ~ "Inconsistent (> 0.001º)")) %>%
  count(coordinate_status)%>%
  mutate(percentage = n / sum(n) * 100) %>%
  ggplot(aes(x = percentage, y = coordinate_status, fill = coordinate_status)) +
  geom_bar(stat = "identity") + 
  geom_text(aes(label = paste0(round(percentage, 1), "%")),
            position = position_stack(vjust = 0.5), size = 3) + 
  labs(x = "Percentage of Plots", y = NULL) +
  theme(axis.text.y = element_text(size = 12)) +
  coord_flip() + theme(legend.position = "none")

Correction ISSUE 1

Text in Ilona’s email: Issue 1: already corrected in the past and for CH_0002 rs_plots filled.

Attached: CH_0002_issue1.txt

correction1<-read_tsv(here("data", "raw", "Data_corrections_Ilona",
                           "CH_0002_issue1.txt"))
Rows: 567 Columns: 13── Column specification ─────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (10): Country, RESURVEY, RS_PROJECT, RS_SITE, RS_PLOT, RS_OBSERV, LOC_METHOD, Dataset, RS...
dbl  (2): PlotObservationID, TV2 relevé number
lgl  (1): RS_DUPL
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Number of rows in correction1 = number of rows in db with that RS_CODE:

nrow(correction1)
[1] 567
nrow(db_resurv %>% filter(RS_CODE == "CH_0002"))
[1] 567

number of rows in db with that RS_CODE and plot as NA = 111. Number of rows in correction1 with plot as NA = 0.

nrow(db_resurv %>% filter(RS_CODE == "CH_0002" & is.na(`ReSurvey plot`)))
[1] 111
nrow(correction1 %>% filter(is.na(RS_PLOT)))
[1] 0

Names in correction1 are different from db:

names(db_resurv)
 [1] "PlotObservationID"        "PlotID"                   "TV2 relevé number"       
 [4] "Country"                  "Biblioreference"          "Nr. table in publ."      
 [7] "Nr. relevé in table"      "Cover abundance scale"    "Author"                  
[10] "Date of recording"        "Syntaxon"                 "Relevé area (m²)"        
[13] "Altitude"                 "Aspect (°)"               "Slope (°)"               
[16] "Cover total (%)"          "Cover tree layer (%)"     "Cover shrub layer (%)"   
[19] "Cover herb layer (%)"     "Cover moss layer (%)"     "Mosses identified (Y/N)" 
[22] "Lichens identified (Y/N)" "Locality"                 "Name association"        
[25] "Name alliance"            "Expert System"            "AUTHOR_NAM"              
[28] "ReSurvey plot (Y/N)"      "For EVA (Y/N)"            "ReSurvey project"        
[31] "ReSurvey site"            "ReSurvey plot"            "ReSurvey observation"    
[34] "Plot shape"               "Manipulate (y/n)"         "Type of manipulation"    
[37] "Location method"          "Data owner"               "RS_CODE"                 
[40] "RS_PROJTYP"               "RS_DUPL"                  "RS_TIME"                 
[43] "Longitude"                "Latitude"                 "Location uncertainty (m)"
[46] "Dataset"                  "Access regime"            "Lon_prec"                
[49] "Lat_prec"                 "precision_new"            "Private"                 
[52] "Lon1"                     "Lon2"                     "Lon3"                    
[55] "Lon4"                     "Lat1"                     "Lat2"                    
[58] "Lat3"                     "Lat4"                     "X"                       
[61] "Y"                        "X1"                       "X2"                      
[64] "X3"                       "X4"                       "Y1"                      
[67] "Y2"                       "Y3"                       "Y4"                      
[70] "Lon_updated"              "Lat_updated"             
names(correction1)
 [1] "PlotObservationID" "TV2 relevé number" "Country"           "RESURVEY"         
 [5] "RS_PROJECT"        "RS_SITE"           "RS_PLOT"           "RS_OBSERV"        
 [9] "LOC_METHOD"        "Dataset"           "RS_CODE"           "RS_PROJTYP"       
[13] "RS_DUPL"          

Rename columns in correction1 to have the same names as in db_resurv.

correction1 <- correction1 %>%
  rename(`ReSurvey plot (Y/N)` = RESURVEY,
         `ReSurvey project` = RS_PROJECT,
         `ReSurvey site` = RS_SITE,
         `ReSurvey plot` = RS_PLOT,
         `ReSurvey observation` = RS_OBSERV,
         `Location method` = LOC_METHOD) %>%
  mutate(RS_DUPL = as.character(RS_DUPL))

Update db_resurv only for cases where ReSurvey plot is NA.

db_resurv_updated <- db_resurv %>%
  # Create a column edit_plot to mark rows to update
  mutate(edit_plot = is.na(`ReSurvey plot`)) %>%
  # Join with correction1 based on PlotObservationID
  # Rename column `Resurvey plot` to avoid joining on this column
  left_join(correction1 %>% select(PlotObservationID, `ReSurvey plot`) %>%
              rename(`ReSurvey plot.new` = `ReSurvey plot`)) %>%
  # Update `ReSurvey plot` with the new values if edit_plot = TRUE
  mutate(`ReSurvey plot` = if_else(edit_plot, 
                                   `ReSurvey plot.new`, `ReSurvey plot`)) %>%
  # Remove unneeded column
  select(-`ReSurvey plot.new`)
Joining with `by = join_by(PlotObservationID)`

Check that there are no rows with ReSurvey plot as NA.

nrow(db_resurv_updated %>% filter(is.na(`ReSurvey plot`)))
[1] 0

Check that there are 111 rows where edit_plot is TRUE.

nrow(db_resurv_updated %>% filter(edit_plot))
[1] 111

Correction ISSUE 2

Text in Ilona’s email: Issue 2: I have prepared remarks for datasets (for many send e-mail to custodian). IT_0008 and DE_0031 corrected

Attached: “200_Issue2_datasets.xlsx”, “DE_0031_coordinatesfilled.xlsx”, “IT_0008_corrected.xlsx”.

In “200_Issue2_datasets.xlsx” there is a list of datasets with coordinates missing. For two of them (a and b below), coordinates are added. For others, Ilona sent an email to owner. For others, there is a remark but not sure if she sent email to owner (wait).

I will only update edits_AV in the cases where coordinates are added.

a

correction2a <- read_excel(
  here("data", "raw", "Data_corrections_Ilona", "Issue2",
       "IT_0008_corrected.xlsx"))
print(correction2a, width = Inf)

Need to update Longitude, Latitude and Location uncertainty (m).

db_resurv_updated <- db_resurv_updated %>%
  # Create a column edit_coords_unc to mark rows to update
  mutate(edit_coords_unc = `ReSurvey plot (Y/N)` == "Y" & RS_CODE =="IT_0008" &
           is.na(Longitude)) %>%
  # Join with correction2a based on PlotObservationID
  # Rename columns to update to avoid joining on these columns
  left_join(
    correction2a %>%
      select(PlotObservationID, Longitude, Latitude,
             `Location uncertainty (m)`) %>%
      rename(Longitude.new = Longitude, Latitude.new = Latitude,
             `Location uncertainty (m).new` = `Location uncertainty (m)`) %>%
      # Set `Location uncertainty (m).new` as numeric
      mutate(`Location uncertainty (m).new` = 
               as.numeric(`Location uncertainty (m).new`))
    ) %>%
  # Update Longitude, Latitude and `Location uncertainty (m)`with the new values
  # if edit_coords_unc = TRUE
  mutate(
    Longitude = if_else(edit_coords_unc, Longitude.new, Longitude),
    Latitude = if_else(edit_coords_unc, Latitude.new, Latitude),
    `Location uncertainty (m)` = if_else(
      edit_coords_unc,`Location uncertainty (m).new`,
      `Location uncertainty (m)`)
    ) %>%
  # Remove unneeded columns
  select(-Longitude.new, -Latitude.new, -`Location uncertainty (m).new`)
Joining with `by = join_by(PlotObservationID)`

Check that there are no rows with RS_CODE == “IT_0008” where Longitude, Latitude and Location uncertainty (m) are NA.

nrow(
  db_resurv_updated %>%
    filter(RS_CODE == "IT_0008") %>%
    filter(is.na(Longitude) | is.na(Latitude) |
             is.na(`Location uncertainty (m)`))
  )
[1] 0

b

correction2b <- read_excel(
  here("data", "raw", "Data_corrections_Ilona", "Issue2",
       "DE_0031_coordinatesfilled.xlsx"))
print(correction2b, width = Inf)

Need to update Longitude, Latitude and Location uncertainty (m) (I guess this is coded as PRECISION).

nrow(correction2b)
[1] 363
nrow(db_resurv_updated %>% filter(RS_CODE == "DE_0031"))
[1] 363
nrow(db_resurv_updated %>% filter(RS_CODE == "DE_0031" & is.na(Longitude)))
[1] 363
nrow(db_resurv_updated %>%
       filter(RS_CODE == "DE_0031" & is.na(`Location uncertainty (m)`)))
[1] 363

All 363 rows with RS_CODE == “DE_0031” have NAs for coordinates and Location uncertainty.

db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(UpdateFlag = `ReSurvey plot (Y/N)` == "Y" & RS_CODE =="DE_0031") %>%
  # Join with correction2b based on PlotObservationID
  # Columns to update already have a different name, 
  # so join will not be done on these columns
  left_join(
    correction2b %>% select(PlotObservationID, LONGITUDE, LATITUDE, PRECISION)
    ) %>%
  # Update Longitude, Latitude and `Location uncertainty (m)`with the new values
  # if UpdateFlag = TRUE
  mutate(
    Longitude = if_else(UpdateFlag, LONGITUDE, Longitude),
    Latitude = if_else(UpdateFlag, LATITUDE, Latitude),
    `Location uncertainty (m)` = if_else(
      UpdateFlag, PRECISION, `Location uncertainty (m)`)
    ) %>%
  # Update column edit_coords_unc to label edits
  mutate(edit_coords_unc = if_else(UpdateFlag, TRUE, edit_coords_unc)) %>%
  # Remove unneeded columns
  select(-UpdateFlag, -LONGITUDE, -LATITUDE, -PRECISION)
Joining with `by = join_by(PlotObservationID)`

Check that there are no rows with RS_CODE == “DE_0031” where Longitude, Latitude and Location uncertainty (m) are NA.

nrow(
  db_resurv_updated %>%
    filter(RS_CODE == "DE_0031") %>%
    filter(is.na(Longitude) | is.na(Latitude) |
             is.na(`Location uncertainty (m)`))
  )
[1] 0

Check how many rows have no coordinates after these updates.

nrow(db_resurv_updated %>%
       filter(`ReSurvey plot (Y/N)` == "Y" & is.na(Longitude)))
[1] 584

Correction ISSUE 3

Text in Ilona’s emails:

Email 1:

Issue3 : again I have prepared remarks for datasets (file 200_Issue3_datasets.xlsx) – in some cases different coordinates for one plot correct. In some cases clear error (Again e-mail to custodians of these datasets) . And also I am sending you files with corrected coordinates. Especially for issue 3 very important is design of resurvey dataset – so please read remark in “200_Issue3_datasets.xlsx”.

Attached: “200_Issue3_datasets.xlsx”, “CZ_0019_048_corrected.xlsx”, “CZ_0019_corrected.xlsx”, “Issue3_CZ_0001_corrected.txt”, “Issue3_ES_0003_corrected.xlsx”.

In “200_Issue3_datasets.xlsx” there is a list of datasets with this issue.

Email 2:

I am sending you corrected coordinate for PL_0009 – one coordinate and coding of 1 plot – changes in red.

Attached: “Issue3_PL_0009_coorected.xlsx”.

I will only update edits_AV in the cases where coordinates are added.

Based on the info in the “remark” column in 200_Issue3_datasets.xlsx”, I created a new column “correct” (in a new file, located in the data/edited folder). This column shows:

I will use this info to update the edits_AV column.

Read the file in the edited data folder.

issue3_datasets <- read_excel(
  here("data", "edited","200_Issue3_datasets_editedAV.xlsx"))

Create two new columns in db_resurv_updated: coordinates_equal indicating if coordinates are exactly equal between ReSurvey observations, and coordinates_consistent, indicating if coordinates are consistent between ReSurvey observations (consistent meaning that difference < 0.001 degrees).

# Define a threshold (e.g., 0.001 degrees for longitude/latitude differences)
threshold <- 0.001

db_resurv_updated <- db_resurv_updated %>%
  group_by(RS_CODE, `ReSurvey site`, `ReSurvey plot`) %>%
  mutate(
    lon_range = ifelse(all(is.na(Lon_updated)), NA,
                        max(Lon_updated, na.rm = T) - 
                         min(Lon_updated, na.rm = T)),
    lat_range = ifelse(all(is.na(Lat_updated)), NA,
                        max(Lat_updated, na.rm = T) - 
                         min(Lat_updated, na.rm = T)),
    coordinates_equal = ifelse(is.na(Lon_updated) & is.na(Lat_updated), NA,
                               lon_range == 0 & lat_range == 0),
    coordinates_consistent = ifelse(is.na(Lon_updated) & is.na(Lat_updated), NA,
                                    lon_range < threshold & 
                                      lat_range < threshold)
  ) %>%
  ungroup() %>%
  select(-lon_range, -lat_range)

See if count of rows in db_resurv_updated where different ReSurvey observations within the same plot have different coordinates matches count in Ilona’s file, and keep cases where row count is different.

issue3_datasets_diff_counts <- db_resurv_updated %>%
  filter(coordinates_equal == FALSE) %>%
  group_by(RS_CODE, Dataset) %>%
  summarize(count = n()) %>%
  ungroup() %>%
  # Add info on Ilona's file
  full_join(
    issue3_datasets %>% select(RS_CODE, Dataset, CountOfDataset) %>%
      mutate(CountOfDataset = as.integer(CountOfDataset))
  ) %>%
  # Keep records where counts are different between db_resurv_updated
  # and Ilona's file
  filter(count != CountOfDataset)
`summarise()` has grouped output by 'RS_CODE'. You can override using the `.groups` argument.Joining with `by = join_by(RS_CODE, Dataset)`
issue3_datasets_diff_counts

These counts might be different because of other changes.

Different coordinates ok / not ok

According to Ilona’s remarks: cases where it is OK / not OK to have different coordinates for different observations of the same plot (OK when resampling, etc).

db_resurv_updated <- db_resurv_updated %>%
  # Join issue3_datasets
  left_join(issue3_datasets %>% select(-CountOfDataset, -remark)) %>%
  # Create a column edit_diff_coords_ok to mark rows to update
  mutate(edit_diff_coords_ok =
           ifelse(
             (coordinates_equal == FALSE | is.na(coordinates_equal)) &
               correct == "ok", TRUE, FALSE)) %>%
  # Create a column edit_diff_coords_not_ok to mark rows to update
  mutate(edit_diff_coords_not_ok =
           ifelse(
             (coordinates_equal == FALSE | is.na(coordinates_equal)) &
               correct == "not_ok", TRUE, FALSE)) %>%
  # Set NA values for edit_diff_coords_ok and edit_diff_coords_not_ok as FALSE
  mutate(edit_diff_coords_ok = ifelse(is.na(edit_diff_coords_ok),
                                      FALSE, edit_diff_coords_ok),
         edit_diff_coords_not_ok = ifelse(is.na(edit_diff_coords_not_ok),
                                      FALSE, edit_diff_coords_not_ok))
Joining with `by = join_by(`ReSurvey project`, RS_CODE, Dataset)`

We have to correct those records where correct == “to_correct” based on Ilona’s files or on info on “200_Issue3_datasets.xlsx”.

Correct coordinates

Which ones to correct?

db_resurv_updated %>% filter(correct == "to_correct") %>%
  distinct(RS_CODE, Dataset, `ReSurvey project`) %>% arrange(RS_CODE)

CZ_0001

Read the file in the raw data folder.

issue3_CZ_0001 <- read_tsv(
  here("data", "raw", "Data_corrections_Ilona", "Issue3",
       "Issue3_CZ_0001_corrected.txt"))
Rows: 60 Columns: 11── Column specification ─────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (6): COUNTRY, RS_PROJECT, RS_SITE, RS_PLOT, RS_OBSERV, RS_CODE
dbl (5): PlotObservationID, DATE, LONGITUDE, LATITUDE, PRECISION
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Need to update Longitude, Latitude and Location uncertainty (m) (I guess this is coded as PRECISION).

Ilona: Please mind that CZ files (all resurvey datasets with rs_codes that begins with “CZ” have special format of coordinates not decimal degrees but DDMMSS.SS).

We need to convert coordinates to decimal degrees.

Function to convert DDMMSS.SS format to decimal degrees:

convert_to_decimal <- function(ddmmss) {
  # Convert to character to handle the data as strings
  ddmmss <- as.character(ddmmss)
  
  # Extract the degree, minute, and second parts
  if (nchar(ddmmss) >= 6) {  # Ensure it's at least 6 characters long (DDMMSS)
    degrees <- as.numeric(substr(ddmmss, 1, 2))  # First 2 digits for degrees
    minutes <- as.numeric(substr(ddmmss, 3, 4))  # Next 2 digits for minutes
    # Rest for seconds (including decimals if any)
    seconds <- as.numeric(substr(ddmmss, 5, nchar(ddmmss)))
  } else {
    return(NA)  # Return NA if the format doesn't match expected
  }
  
  # Convert to decimal degrees
  decimal_degrees <- degrees + (minutes / 60) + (seconds / 3600)
  
  return(decimal_degrees)
}

Apply the conversion function to the longitude and latitude columns:

issue3_CZ_0001 <- issue3_CZ_0001 %>%
  mutate(LONGITUDE_decimal = sapply(LONGITUDE, convert_to_decimal),
         LATITUDE_decimal = sapply(LATITUDE, convert_to_decimal))

Number of rows in issue3_CZ_0001 = number of rows in db with that RS_CODE:

nrow(issue3_CZ_0001)
[1] 60
nrow(db_resurv_updated %>% filter(RS_CODE == "CZ_0001"))
[1] 60

Update db_resurv_updated only for cases where RS_CODE == “CZ_0001”.

db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(UpdateFlag = RS_CODE == "CZ_0001") %>%
  # Join with issue3_CZ_0001 based on PlotObservationID
  left_join(issue3_CZ_0001 %>%
              select(PlotObservationID, LONGITUDE_decimal, LATITUDE_decimal,
                     PRECISION)) %>%
  # Update Longitude, Latitude and `Location uncertainty (m)`with the new values
  # if UpdateFlag = TRUE
  mutate(
    Longitude = if_else(UpdateFlag, LONGITUDE_decimal, Longitude),
    Latitude = if_else(UpdateFlag, LATITUDE_decimal, Latitude),
    `Location uncertainty (m)` = if_else(
      UpdateFlag, PRECISION, `Location uncertainty (m)`)
    ) %>%
  # Update column edit_coords_unc to label edits
  mutate(edit_coords_unc = if_else(UpdateFlag, TRUE, edit_coords_unc)) %>%
  # Remove unneeded columns
  select(-UpdateFlag, -LONGITUDE_decimal, -LATITUDE_decimal, -PRECISION)
Joining with `by = join_by(PlotObservationID)`

CZ_0005

nrow(db_resurv_updated %>%
       filter(RS_CODE == "CZ_0005" & coordinates_equal == FALSE))
[1] 6

Ilona’s remark: the last observation of this plot was corrected same coordinates as all other observations.

db_resurv_updated %>%
  filter(RS_CODE == "CZ_0005" & coordinates_equal == FALSE) %>%
  select(RS_CODE, `ReSurvey plot`, `ReSurvey observation`, Longitude, Latitude)

Get values of longitude and latitude from all other observations to correct the last observation of this plot:

longitude <- as.numeric(
  db_resurv_updated %>%
    filter(RS_CODE == "CZ_0005" & coordinates_equal == FALSE) %>%
    select(RS_CODE, `ReSurvey plot`, `ReSurvey observation`, Longitude, Latitude) %>%
    mutate(`ReSurvey observation` = as.numeric(`ReSurvey observation`)) %>%
    filter(`ReSurvey observation` == min(`ReSurvey observation`)) %>%
    select(Longitude)
  )

latitude <- as.numeric(
  db_resurv_updated %>%
    filter(RS_CODE == "CZ_0005" & coordinates_equal == FALSE) %>%
    select(RS_CODE, `ReSurvey plot`, `ReSurvey observation`, Longitude, Latitude) %>%
    mutate(`ReSurvey observation` = as.numeric(`ReSurvey observation`)) %>%
    filter(`ReSurvey observation` == min(`ReSurvey observation`)) %>%
    select(Latitude)
)

Correct the last observation of this plot:

db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark the row to update
  mutate(UpdateFlag = RS_CODE == "CZ_0005" & coordinates_equal == FALSE &
           `ReSurvey observation` == 2021) %>%
  # Update Longitude and Latitude with the values above if UpdateFlag = TRUE
  mutate(
    Longitude = if_else(UpdateFlag, longitude, Longitude),
    Latitude = if_else(UpdateFlag, latitude, Latitude)
    ) %>%
  # Create a column edit_coords to mark the row to update
  mutate(edit_coords = RS_CODE == "CZ_0005" & coordinates_equal == FALSE &
           `ReSurvey observation` == 2021) %>%
  # Remove unneeded column
  select(-UpdateFlag)

CZ_0019

Read the file in the raw data folder.

issue3_CZ_0019 <- read_excel(
  here("data", "raw", "Data_corrections_Ilona", "Issue3",
       "CZ_0019_corrected.xlsx"))

Need to update Longitude, Latitude and Location uncertainty (m) (I guess this is coded as PRECISION).

Ilona: Please mind that CZ files (all resurvey datasets with rs_codes that begins with “CZ” have special format of coordinates not decimal degrees but DDMMSS.SS).

We need to convert coordinates to decimal degrees.

Apply the conversion function to the longitude and latitude columns:

issue3_CZ_0019 <- issue3_CZ_0019 %>%
  mutate(LONGITUDE_decimal = sapply(LONGITUDE, convert_to_decimal),
         LATITUDE_decimal = sapply(LATITUDE, convert_to_decimal))

CZ_0019_002

nrow(issue3_CZ_0019 %>% filter(RS_CODE == "CZ_0019_002"))
[1] 2
nrow(db_resurv_updated %>%
       filter(RS_CODE == "CZ_0019_002" & coordinates_equal == FALSE))
[1] 2

Update db_resurv_updated.

db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(UpdateFlag = RS_CODE == "CZ_0019_002" & coordinates_equal == FALSE) %>%
  # Join with issue3_CZ_0019 based on RELEVE_NR
  left_join(issue3_CZ_0019 %>%
              select(RELEVE_NR, LONGITUDE_decimal, LATITUDE_decimal,
                     PRECISION),
            join_by(`TV2 relevé number` == RELEVE_NR)) %>%
  # Update Longitude, Latitude and `Location uncertainty (m)`with the new values
  # if UpdateFlag = TRUE
  mutate(
    Longitude = if_else(UpdateFlag, LONGITUDE_decimal, Longitude),
    Latitude = if_else(UpdateFlag, LATITUDE_decimal, Latitude),
    `Location uncertainty (m)` = if_else(
      UpdateFlag, PRECISION, `Location uncertainty (m)`)
    ) %>%
  # Update column edit_coords_unc to label edits
  mutate(edit_coords_unc = if_else(UpdateFlag, TRUE, edit_coords_unc)) %>%
  # Remove unneeded columns
  select(-UpdateFlag, -LONGITUDE_decimal, -LATITUDE_decimal, -PRECISION)

CZ_0019_013

nrow(issue3_CZ_0019 %>% filter(RS_CODE == "CZ_0019_013"))
[1] 35
nrow(db_resurv_updated %>%
       filter(RS_CODE == "CZ_0019_013"))
[1] 35

Update db_resurv_updated.

db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(UpdateFlag = RS_CODE == "CZ_0019_013") %>%
  # Join with issue3_CZ_0019 based on RELEVE_NR
  left_join(issue3_CZ_0019 %>%
              select(RELEVE_NR, LONGITUDE_decimal, LATITUDE_decimal,
                     PRECISION),
            join_by(`TV2 relevé number` == RELEVE_NR)) %>%
  # Update Longitude, Latitude and `Location uncertainty (m)`with the new values
  # if UpdateFlag = TRUE
  mutate(
    Longitude = if_else(UpdateFlag, LONGITUDE_decimal, Longitude),
    Latitude = if_else(UpdateFlag, LATITUDE_decimal, Latitude),
    `Location uncertainty (m)` = if_else(
      UpdateFlag, PRECISION, `Location uncertainty (m)`)
    ) %>%
  # Update column edit_coords_unc to label edits
  mutate(edit_coords_unc = if_else(UpdateFlag, TRUE, edit_coords_unc)) %>%
  # Remove unneeded columns
  select(-UpdateFlag, -LONGITUDE_decimal, -LATITUDE_decimal, -PRECISION)

CZ_0019_019

issue3_CZ_0019 %>% filter(RS_CODE == "CZ_0019_019")
nrow(db_resurv_updated %>%
       filter(RS_CODE == "CZ_0019_019" & coordinates_equal == FALSE &
                `TV2 relevé number` == 907358))
[1] 1

Update db_resurv_updated.

db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(UpdateFlag = RS_CODE == "CZ_0019_019" & coordinates_equal == FALSE &
                `TV2 relevé number` == 907358) %>%
  # Join with issue3_CZ_0019 based on RELEVE_NR
  left_join(issue3_CZ_0019 %>%
              select(RELEVE_NR, LONGITUDE_decimal, LATITUDE_decimal,
                     PRECISION),
            join_by(`TV2 relevé number` == RELEVE_NR)) %>%
  # Update Longitude, Latitude and `Location uncertainty (m)`with the new values
  # if UpdateFlag = TRUE
  mutate(
    Longitude = if_else(UpdateFlag, LONGITUDE_decimal, Longitude),
    Latitude = if_else(UpdateFlag, LATITUDE_decimal, Latitude),
    `Location uncertainty (m)` = if_else(
      UpdateFlag, PRECISION, `Location uncertainty (m)`)
    ) %>%
  # Update column edit_coords_unc to label edits
  mutate(edit_coords_unc = if_else(UpdateFlag, TRUE, edit_coords_unc)) %>%
  # Remove unneeded columns
  select(-UpdateFlag, -LONGITUDE_decimal, -LATITUDE_decimal, -PRECISION)

CZ_0019_034

nrow(issue3_CZ_0019 %>% filter(RS_CODE == "CZ_0019_034"))
[1] 2
nrow(db_resurv_updated %>%
       filter(RS_CODE == "CZ_0019_034"))
[1] 2

Update db_resurv_updated.

db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(UpdateFlag = RS_CODE == "CZ_0019_034") %>%
  # Join with issue3_CZ_0019 based on RELEVE_NR
  left_join(issue3_CZ_0019 %>%
              select(RELEVE_NR, LONGITUDE_decimal, LATITUDE_decimal,
                     PRECISION),
            join_by(`TV2 relevé number` == RELEVE_NR)) %>%
  # Update Longitude, Latitude and `Location uncertainty (m)`with the new values
  # if UpdateFlag = TRUE
  mutate(
    Longitude = if_else(UpdateFlag, LONGITUDE_decimal, Longitude),
    Latitude = if_else(UpdateFlag, LATITUDE_decimal, Latitude),
    `Location uncertainty (m)` = if_else(
      UpdateFlag, PRECISION, `Location uncertainty (m)`)
    ) %>%
  # Update column edit_coords_unc to label edits
  mutate(edit_coords_unc = if_else(UpdateFlag, TRUE, edit_coords_unc)) %>%
  # Remove unneeded columns
  select(-UpdateFlag, -LONGITUDE_decimal, -LATITUDE_decimal, -PRECISION)

CZ_0019_035

nrow(issue3_CZ_0019 %>% filter(RS_CODE == "CZ_0019_035"))
[1] 12
nrow(db_resurv_updated %>%
       filter(RS_CODE == "CZ_0019_035"))
[1] 12

Update db_resurv_updated.

db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(UpdateFlag = RS_CODE == "CZ_0019_035") %>%
  # Join with issue3_CZ_0019 based on RELEVE_NR
  left_join(issue3_CZ_0019 %>%
              select(RELEVE_NR, LONGITUDE_decimal, LATITUDE_decimal,
                     PRECISION),
            join_by(`TV2 relevé number` == RELEVE_NR)) %>%
  # Update Longitude, Latitude and `Location uncertainty (m)`with the new values
  # if UpdateFlag = TRUE
  mutate(
    Longitude = if_else(UpdateFlag, LONGITUDE_decimal, Longitude),
    Latitude = if_else(UpdateFlag, LATITUDE_decimal, Latitude),
    `Location uncertainty (m)` = if_else(
      UpdateFlag, PRECISION, `Location uncertainty (m)`)
    ) %>%
  # Update column edit_coords_unc to label edits
  mutate(edit_coords_unc = if_else(UpdateFlag, TRUE, edit_coords_unc)) %>%
  # Remove unneeded columns
  select(-UpdateFlag, -LONGITUDE_decimal, -LATITUDE_decimal, -PRECISION)

CZ_0019_041

Read the file in the raw data folder.

issue3_CZ_0019_041 <- read_excel(
  here("data", "raw", "Data_corrections_Ilona", "Issue3",
       "CZ_0019_041_corrected.xlsx"))

Need to update Longitude, Latitude and Location uncertainty (m) (I guess this is coded as PRECISION).

Ilona: Please mind that CZ files (all resurvey datasets with rs_codes that begins with “CZ” have special format of coordinates not decimal degrees but DDMMSS.SS).

We need to convert coordinates to decimal degrees.

Apply the conversion function to the longitude and latitude columns:

issue3_CZ_0019_041 <- issue3_CZ_0019_041 %>%
  mutate(LONGITUDE_decimal = sapply(LONGITUDE, convert_to_decimal),
         LATITUDE_decimal = sapply(LATITUDE, convert_to_decimal))
nrow(issue3_CZ_0019_041)
[1] 2
nrow(db_resurv_updated %>%
       filter(RS_CODE == "CZ_0019_041" & coordinates_equal == FALSE))
[1] 2

Update db_resurv_updated.

db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(UpdateFlag = RS_CODE == "CZ_0019_041" & coordinates_equal == FALSE) %>%
  # Join with issue3_CZ_0019_041 based on RELEVE_NR
  left_join(issue3_CZ_0019_041 %>%
              select(RELEVE_NR, LONGITUDE_decimal, LATITUDE_decimal,
                     PRECISION),
            join_by(`TV2 relevé number` == RELEVE_NR)) %>%
  # Update Longitude, Latitude and `Location uncertainty (m)`with the new values
  # if UpdateFlag = TRUE
  mutate(
    Longitude = if_else(UpdateFlag, LONGITUDE_decimal, Longitude),
    Latitude = if_else(UpdateFlag, LATITUDE_decimal, Latitude),
    `Location uncertainty (m)` = if_else(
      UpdateFlag, PRECISION, `Location uncertainty (m)`)
    ) %>%
  # Update column edit_coords_unc to label edits
  mutate(edit_coords_unc = if_else(UpdateFlag, TRUE, edit_coords_unc)) %>%
  # Remove unneeded columns
  select(-UpdateFlag, -LONGITUDE_decimal, -LATITUDE_decimal, -PRECISION)

CZ_0019_048

Read the file in the raw data folder.

issue3_CZ_0019_048 <- read_excel(
  here("data", "raw", "Data_corrections_Ilona", "Issue3",
       "CZ_0019_048_corrected.xlsx"))

Need to update Longitude, Latitude and Location uncertainty (m) (I guess this is coded as PRECISION).

Ilona: Please mind that CZ files (all resurvey datasets with rs_codes that begins with “CZ” have special format of coordinates not decimal degrees but DDMMSS.SS).

We need to convert coordinates to decimal degrees.

Apply the conversion function to the longitude and latitude columns:

issue3_CZ_0019_048 <- issue3_CZ_0019_048 %>%
  mutate(LONGITUDE_decimal = sapply(LONGITUDE, convert_to_decimal),
         LATITUDE_decimal = sapply(LATITUDE, convert_to_decimal))
nrow(issue3_CZ_0019_048)
[1] 58
nrow(db_resurv_updated %>%
       filter(RS_CODE == "CZ_0019_048" & coordinates_equal == FALSE))
[1] 58

Update db_resurv_updated.

db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(UpdateFlag = RS_CODE == "CZ_0019_048" & coordinates_equal == FALSE) %>%
  # Join with issue3_CZ_0019_048 based on PlotObservationID
  left_join(issue3_CZ_0019_048 %>%
              select(PlotObservationID, LONGITUDE_decimal, LATITUDE_decimal,
                     PRECISION)) %>%
  # Update Longitude, Latitude and `Location uncertainty (m)`with the new values
  # if UpdateFlag = TRUE
  mutate(
    Longitude = if_else(UpdateFlag, LONGITUDE_decimal, Longitude),
    Latitude = if_else(UpdateFlag, LATITUDE_decimal, Latitude),
    `Location uncertainty (m)` = if_else(
      UpdateFlag, PRECISION, `Location uncertainty (m)`)
    ) %>%
  # Update column edit_coords_unc to label edits
  mutate(edit_coords_unc = if_else(UpdateFlag, TRUE, edit_coords_unc)) %>%
  # Remove unneeded columns
  select(-UpdateFlag, -LONGITUDE_decimal, -LATITUDE_decimal, -PRECISION)
Joining with `by = join_by(PlotObservationID)`

ES_0003

Read the file in the raw data folder.

issue3_ES_0003 <- read_excel(
  here("data", "raw", "Data_corrections_Ilona", "Issue3",
       "Issue3_ES_0003_corrected.xlsx"))
nrow(issue3_ES_0003)
[1] 14
nrow(db_resurv_updated %>%
       filter(RS_CODE == "ES_0003" & coordinates_equal == FALSE))
[1] 14

Update db_resurv_updated.

db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(UpdateFlag = RS_CODE == "ES_0003" & coordinates_equal == FALSE) %>%
  # Join with issue3_ES_0003 based on PlotObservationID
  left_join(issue3_ES_0003 %>%
              select(PlotObservationID, LONGITUDE, LATITUDE,
                     PRECISION)) %>%
  # Update Longitude, Latitude and `Location uncertainty (m)`with the new values
  # if UpdateFlag = TRUE
  mutate(
    Longitude = if_else(UpdateFlag, LONGITUDE, Longitude),
    Latitude = if_else(UpdateFlag, LATITUDE, Latitude),
    `Location uncertainty (m)` = if_else(
      UpdateFlag, PRECISION, `Location uncertainty (m)`)
    ) %>%
  # Update column edit_coords_unc to label edits
  mutate(edit_coords_unc = if_else(UpdateFlag, TRUE, edit_coords_unc)) %>%
  # Remove unneeded columns
  select(-UpdateFlag, -LONGITUDE, -LATITUDE, -PRECISION)
Joining with `by = join_by(PlotObservationID)`

PL_0009

Read the file in the raw data folder (I manually corrected the coordiantes of the last 4 rows of this file because latitude and longitude were mxied - Ilona sent an email about this).

issue3_PL_0009 <- read_excel(
  here("data", "edited", "Issue3_PL_0009_coorected_corrAV.xlsx"))
nrow(issue3_PL_0009)
[1] 7
nrow(db_resurv_updated %>%
       filter(RS_CODE == "PL_0009" & coordinates_consistent == FALSE))
[1] 7

Ilona’s email: I am sending you corrected coordinate for PL_0009 – one coordinate and coding of 1 plot – changes in red.

According to info in Excel file (), need to update Longitude, Latitude and ReSurvey plot.

Update db_resurv_updated.

db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(UpdateFlag = RS_CODE == "PL_0009" & coordinates_consistent == FALSE) %>%
  # Join with issue3_PL_0009 based on PlotObservationID
  left_join(issue3_PL_0009 %>%
              select(PlotObservationID, `ReSurvey plot`, Longitude, Latitude) %>%
              # Rename columns `Resurvey plot`, Longitude and Latitude
              # to avoid joining on these columns
              rename(`ReSurvey plot.new` = `ReSurvey plot`,
                     Longitude.new = Longitude, Latitude.new = Latitude)) %>%
  # Update Longitude, Latitude and `ReSurvey plot`with the new values
  # if UpdateFlag = TRUE
  mutate(
    Longitude = if_else(UpdateFlag, Longitude.new, Longitude),
    Latitude = if_else(UpdateFlag, Latitude.new, Latitude),
    `ReSurvey plot` = if_else(
      UpdateFlag, `ReSurvey plot.new`, `ReSurvey plot`)
    ) %>%
  # Update column edit_coords to label edits
  mutate(edit_coords = if_else(UpdateFlag, TRUE, edit_coords)) %>%
  # Update column edit_pplot to label edits
  mutate(edit_plot = if_else(UpdateFlag, TRUE, edit_plot)) %>%
  # Remove unneeded columns
  select(-UpdateFlag, -Longitude.new, -Latitude.new, -`ReSurvey plot.new`)
Joining with `by = join_by(PlotObservationID)`

IT_0004

Text in Ilona’s email: One correction for issue4 – 6 coordinates are corrected – however this DB – has another problem with rs_codes (seems to me that original coding of plots are wrong – that´s why you have not repeating plots) and also one group of observation has also dubious coordinates. I am waiting for another corrections.

Issue3_IT_0004 <- read_excel(
  here("data", "raw", "Data_corrections_Ilona", "Issue3",
       "Issue3_IT_0004_corected.xlsx"))
db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(UpdateFlag = PlotObservationID == 339442 |
           PlotObservationID == 339443 | PlotObservationID ==339441) %>%
  # Join with Issue3_IT_0004 based on PlotObservationID
  left_join(Issue3_IT_0004 %>%
              mutate(Longitude_new = Longitude, Latitude_new = Latitude) %>%
              select(PlotObservationID, Longitude_new, Latitude_new)) %>%
  # Update Longitude and Latitude with the new values
  # if UpdateFlag = TRUE
  mutate(
    Longitude = if_else(UpdateFlag, Longitude_new, Longitude),
    Latitude = if_else(UpdateFlag, Latitude_new, Latitude)
    ) %>%
  # Update column edit_coords to label edits
  mutate(edit_coords = if_else(UpdateFlag, TRUE, edit_coords)) %>%
  # Remove unneeded columns
  select(-UpdateFlag, -Longitude_new, -Latitude_new)
Joining with `by = join_by(PlotObservationID)`

Correction confirmation

Confirm that all that needed to be corrected have been corrected:

all.equal(
  db_resurv_updated %>% filter(correct == "to_correct") %>%
    distinct(RS_CODE, Dataset, `ReSurvey project`) %>% arrange(RS_CODE),
  db_resurv_updated %>% filter(correct == "to_correct") %>%
    distinct(RS_CODE, Dataset, `ReSurvey project`) %>% arrange(RS_CODE)
  )
[1] TRUE

Remove unneeded column and recalculate some variables

db_resurv_updated <- db_resurv_updated %>%
  # Remove "correct" column (not needed anymore)
  select(-correct) %>%
  # Recalculate updated coordinates
  mutate(Lon_updated = ifelse(is.na(Lon_prec), Longitude, Lon_prec),
         Lat_updated = ifelse(is.na(Lat_prec), Latitude, Lat_prec)) %>%
  # Recalculate coordinates_equal and coordinates_consistent
  group_by(RS_CODE, `ReSurvey site`, `ReSurvey plot`) %>%
  mutate(
    lon_range = ifelse(all(is.na(Lon_updated)), NA,
                       max(Lon_updated, na.rm = T) - 
                         min(Lon_updated, na.rm = T)),
    lat_range = ifelse(all(is.na(Lat_updated)), NA,
                       max(Lat_updated, na.rm = T) - 
                         min(Lat_updated, na.rm = T)),
    coordinates_equal = ifelse(is.na(Lon_updated) & is.na(Lat_updated), NA,
                               lon_range == 0 & lat_range == 0),
    coordinates_consistent = ifelse(is.na(Lon_updated) & is.na(Lat_updated), NA,
                                    lon_range < threshold &
                                      lat_range < threshold)
    ) %>%
  ungroup() %>%
  select(-lon_range, -lat_range)

Updated barplot of coordinate status

db_resurv_updated %>% 
  group_by(RS_CODE,`ReSurvey site`, `ReSurvey plot`) %>%
  summarize(is_equal = all(coordinates_equal),
            is_consistent = all(coordinates_consistent),
            .groups = "drop") %>%
  mutate(coordinate_status = case_when(
    is_equal ~ "Equal",
    !is_equal & is_consistent ~ "Consistent (< 0.001º)",
    !is_equal & !is_consistent ~ "Inconsistent (> 0.001º)")) %>%
  count(coordinate_status)%>%
  mutate(percentage = n / sum(n) * 100) %>%
  ggplot(aes(x = percentage, y = coordinate_status, fill = coordinate_status)) +
  geom_bar(stat = "identity") + 
  geom_text(aes(label = paste0(round(percentage, 1), "%")),
            position = position_stack(vjust = 0.5), size = 3) + 
  labs(x = "Percentage of Plots", y = NULL) +
  theme(axis.text.y = element_text(size = 12)) +
  coord_flip() + theme(legend.position = "none")

Correction ISSUE 4

Count resurveys

count_resurveys <- db_resurv_updated %>%
  # Convert dates to date format and get the year
  mutate(date = dmy(`Date of recording`), year = year(date)) %>%
 # Group by RS_CODE, `ReSurvey site`, `ReSurvey plot`
  group_by(RS_CODE, `ReSurvey site`, `ReSurvey plot`) %>%
  summarise(
    # Get how many different years for each unique group
    distinct_years=n_distinct(year), 
    # Get how many different dates for each unique group
    distinct_dates=n_distinct(date), .groups = "drop")

Summary stats:

summary(count_resurveys$distinct_years)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  1.000   2.000   2.000   3.449   4.000  55.000 
sd(count_resurveys$distinct_years)
[1] 2.677606

Histograms:

# For all data
ggplot(count_resurveys, aes(x = distinct_years)) + 
  geom_histogram(fill = "white", color = "black", bins = 55)+
  xlab("Number of ReSurvey observations (different years)") +
  ylab("Number of plots")

Number and proportion of plots with only 1 resurvey (should not be so!)

nrow(count_resurveys%>%filter(distinct_years==1))
[1] 1145
nrow(count_resurveys%>%filter(distinct_years==1))/nrow(count_resurveys)
[1] 0.009467035

Ilona sent file “issue4_remarks.xlsx”.

Based on the info in the “remark” column in this file, I created a new column “correct” (in a new file, located in the data/edited folder). This column shows:

  • manually: these rows should be manually corrected based on the info on the “remark” column

  • not_resurv: these rows do not belong to resurveys, so ReSurvey plot (Y/N) should be changed to N, and these rows should be later added to db_EVA

  • remove: these rows have different designs and should be excluded

  • wait: Ilona sent email to custodian and we are waiting for response

Read the file in the edited data folder.

issue4_remarks <- read_excel(
  here("data", "edited","issue4_remarks_editedAV.xlsx"))

not_resurv

db_resurv_updated <- db_resurv_updated %>%
  # Join issue4_remarks
  left_join(
    issue4_remarks %>%
      select(RS_CODE, `ReSurvey site`, `ReSurvey plot`, correct)
    ) %>%
  # Create a column edit_not_resurv to mark rows to update
  mutate(edit_not_resurv = ifelse(correct == "not_resurv", TRUE, FALSE)) %>%
  # Set NA values for edit_not_resurv as FALSE
  mutate(edit_not_resurv = ifelse(is.na(edit_not_resurv),
                                  FALSE, edit_not_resurv))
Joining with `by = join_by(`ReSurvey site`, `ReSurvey plot`, RS_CODE)`

Save rows to add later to EVA-db.

write_tsv(db_resurv_updated %>% filter(edit_not_resurv == T),
          here("data", "clean","db_add_to_EVA.csv"))

Remove those rows from db_resurv_updated and remove column “edit_not_resurv”.

db_resurv_updated <- db_resurv_updated %>%
  filter(edit_not_resurv == F) %>% select(-edit_not_resurv)

remove

Remove rows where column “correct” is equal to “remove”.

db_resurv_updated <- db_resurv_updated %>%
  filter(correct != "remove" | is.na(correct))

manually

nrows_corr_manually <- nrow(db_resurv_updated %>% filter(correct == "manually"))

I need to correct nrows_corr_manually rows manually.

db_resurv_updated %>% filter(correct == "manually") %>% count(RS_CODE)

AT_0001

count_resurveys %>% filter(RS_CODE == "AT_0001" & distinct_years == 1)

Remark in Ilona’s file: wrong year for 1 observation, see rs_time (when value 1, yoer should be 2003) - typing mistake.

Update db_resurv_updated.

db_resurv_updated <- db_resurv_updated %>%
  # Create a column edit_date to mark rows to update
  mutate(edit_date = RS_CODE == "AT_0001" &
           (`ReSurvey plot` == "17" | `ReSurvey plot` == "A_2955") &
           RS_TIME == 1) %>%
  # Update `Date of recording` if edit_date = TRUE
  mutate(
    `Date of recording` = if_else(
      edit_date,
      str_replace_all(`Date of recording`, "2022", "2003"),
      `Date of recording`)
    )

CZ_0001

Remark in Ilona’s file: Repeated RS_plot should be Slana1.

Change ReSurvey site from “Slana1” to “Slana”.

Update db_resurv_updated.

db_resurv_updated <- db_resurv_updated %>%
  # Create a column edit_date to mark rows to update
  mutate(edit_site = `ReSurvey site` == "Slana1") %>%
  # Update `ReSurvey site` if edit_site = TRUE
  mutate(
    `ReSurvey site` = if_else(
      edit_site, 
      str_replace_all(`ReSurvey site`, "Slana1", "Slana"),
      `ReSurvey site`)
    )

ES_0001d

Remark in Ilona’s file: is repeated, corrected date (I remember sometimes last year).

Correct date of second resurvey (RS_TIME == 2) to 2018.

Update db_resurv_updated.

db_resurv_updated <- db_resurv_updated %>%
  # Approach creating an UpdateFlag assigns NAs to several dates, not sure why!
  # Update `Date of recording` just for the particular PlotObservationID
  mutate(
    `Date of recording` = if_else(
      PlotObservationID == "523615",
      str_replace_all(`Date of recording`, "2005", "2018"),
      `Date of recording`)
    ) %>%
  # Update column edit_date to label edits
  mutate(edit_date = if_else(PlotObservationID == "523615", TRUE, edit_date))

FR_0002f

Remark in Ilona’s file: corrected name of this plot.

Change ReSurvey plot from “PSET3-4” to “PSET 3-4”

Update db_resurv_updated.

db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(UpdateFlag = `ReSurvey plot` == "PSET3-4") %>%
  # Update `ReSurvey plot` if UpdateFlag = TRUE
  mutate(
    `ReSurvey plot` = if_else(UpdateFlag, "PSET 3-4", `ReSurvey plot`)
    ) %>%
  # Update column edit_plot to label edits
  mutate(edit_plot = if_else(UpdateFlag, TRUE, edit_plot)) %>%
  # Remove unneeded column
  select(-UpdateFlag)

FR_0002h

Remark in Ilona’s file: correcte dname for PSET T2 5-6, repeated 3x

Change ReSurvey plot from “PSET T2 5-6” to “PSET T2 5-6”.

Update db_resurv_updated.

db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(UpdateFlag = `ReSurvey plot` == "PSET T2  5-6") %>%
  # Update `ReSurvey plot` if UpdateFlag = TRUE
  mutate(
    `ReSurvey plot` = if_else(UpdateFlag, "PSET T2 5-6", `ReSurvey plot`)
    ) %>%
  # Update column edit_plot to label edits
  mutate(edit_plot = if_else(UpdateFlag, TRUE, edit_plot)) %>%
  # Remove unneeded column
  select(-UpdateFlag)

IT_0001c

Remark in Ilona’s file: chnnge of coding RS_plot, some plots have index a (same area as old observ) or b (smaller Area), indexes kept in rs_observ.

Remove “b” and “a” from ReSurvey plot.

Update db_resurv_updated.

db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(
    UpdateFlag = RS_CODE == "IT_0001c" & correct == "manually" &
      (str_detect(`ReSurvey plot`, "a") | str_detect(`ReSurvey plot`, "b"))
    ) %>%
  # Update `ReSurvey plot` if UpdateFlag = TRUE
  mutate(
    `ReSurvey plot` = if_else(
      UpdateFlag,
      str_replace_all(`ReSurvey plot`, "[ab]", ""),  # Matches both "a" and "b"
      `ReSurvey plot`)
    ) %>%
  # Update column edit_plot to label edits
  mutate(edit_plot = if_else(UpdateFlag, TRUE, edit_plot)) %>%
  # Remove unneeded column
  select(-UpdateFlag)

Also change ReSurvey plot 894 to 89 which seems to be the correct number.

db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(
    UpdateFlag = RS_CODE == "IT_0001c" & correct == "manually" &
      `ReSurvey plot` == "894"
    ) %>%
  # Update `ReSurvey plot` if UpdateFlag = TRUE
  mutate(`ReSurvey plot` = if_else(UpdateFlag, "89", `ReSurvey plot`)) %>%
  # Update column edit_plot to label edits
  mutate(edit_plot = if_else(UpdateFlag, TRUE, edit_plot)) %>%
  # Remove unneeded column
  select(-UpdateFlag)

IT_0001d

Remark in Ilona’s file: 1 old correspond to more new observation A,B,C - removed indexes from plots, kept in observations.

Remove “A”, “B” and “C” from ReSurvey plot.

Update db_resurv_updated.

db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(
    UpdateFlag = RS_CODE == "IT_0001d" & correct == "manually" &
      (str_detect(`ReSurvey plot`, "A") | str_detect(`ReSurvey plot`, "B") |
         str_detect(`ReSurvey plot`, "C"))
    ) %>%
  # Update `ReSurvey plot` if UpdateFlag = TRUE
  mutate(
    `ReSurvey plot` = if_else(
      UpdateFlag,
      str_replace_all(`ReSurvey plot`, "[ABC]", ""),
      `ReSurvey plot`)
    ) %>%
  # Update column edit_plot to label edits
  mutate(edit_plot = if_else(UpdateFlag, TRUE, edit_plot)) %>%
  # Remove unneeded column
  select(-UpdateFlag)

IT_0001e

Remark in Ilona’s file: change of RS_PLOTS (1 original 1O, more new 1s = standard size 100m, a, b, c = same, larger size).

Remove “a”, “b”, “c” and “s” from ReSurvey plot.

Update db_resurv_updated.

db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(
    UpdateFlag = RS_CODE == "IT_0001e" & correct == "manually" &
      (str_detect(`ReSurvey plot`, "a") | str_detect(`ReSurvey plot`, "b") |
         str_detect(`ReSurvey plot`, "c") | str_detect(`ReSurvey plot`, "s"))
    ) %>%
  # Update `ReSurvey plot` if UpdateFlag = TRUE
  mutate(
    `ReSurvey plot` = if_else(
      UpdateFlag,
      str_replace_all(`ReSurvey plot`, "[abcs]", ""),
      `ReSurvey plot`)
    ) %>%
  # Update column edit_plot to label edits
  mutate(edit_plot = if_else(UpdateFlag, TRUE, edit_plot)) %>%
  # Remove unneeded column
  select(-UpdateFlag)

LV_0001b

Remark in Ilona’s file: changed for B10, !! For whole dataset I changed RS_codes - added plot sizes - to be able to link same size through years, nested design.

Change ReSurvey plot b10 to B10.

db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(
    UpdateFlag = RS_CODE == "LV_0001b" & correct == "manually" &
      `ReSurvey plot` == "b10"
    ) %>%
  # Update `ReSurvey plot` if UpdateFlag = TRUE
  mutate(`ReSurvey plot` = if_else(UpdateFlag, "B10", `ReSurvey plot`)) %>%
  # Update column edit_plot to label edits
  mutate(edit_plot = if_else(UpdateFlag, TRUE, edit_plot)) %>%
  # Remove unneeded column
  select(-UpdateFlag)

All rows have Relevé area (m²). Later we should select rows with the same Relevé area (m²) throughout the years, and discard others.

For cases where there are multiple observations per year, I can use Relevé area (m²) to select one of the observations (the one that has an area equal to the area in other years).

NO_0001

Remark in Ilona’s file: resampling N-N, both old and new from same locality, rs_plot changed for Ullerengsanden_Ullerengslaguna, precision ofr old set for 1000 m.

Change ReSurvey plot Ullerengsanden and Ullerengslaguna to Ullerengsanden_Ullerengslaguna.

db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(
    UpdateFlag = RS_CODE == "NO_0001" & correct == "manually" &
      (`ReSurvey plot` == "Ullerengsanden" | 
         `ReSurvey plot` == "Ullerengslaguna")
    ) %>%
  # Update `ReSurvey plot` if UpdateFlag = TRUE
  mutate(`ReSurvey plot` = if_else(UpdateFlag, "Ullerengsanden_Ullerengslaguna",
                                   `ReSurvey plot`)) %>%
  # Update column edit_plot to label edits
  mutate(edit_plot = if_else(UpdateFlag, TRUE, edit_plot)) %>%
  # Change Location uncertainty (m) to 1000 m when RS_TIME == 1
  # Create a column edit_unc to mark rows to update
  mutate(edit_unc = UpdateFlag & RS_TIME == 1) %>%
  # Update `Location uncertainty (m)` if edit_unc = TRUE
  mutate(`Location uncertainty (m)` = ifelse(edit_unc, 1000,
                                             `Location uncertainty (m)`)) %>%
# Remove unneeded column
  select(-UpdateFlag)

SI_0002a

Remark in Ilona’s file: error in RS_SITE changed for RF.

Change ReSurvey site FS to RS.

db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(
    UpdateFlag = RS_CODE == "SI_0002a" & correct == "manually" &
      `ReSurvey site` == "FS"
    ) %>%
  # Update `ReSurvey site` if UpdateFlag = TRUE
  mutate(`ReSurvey site` = if_else(UpdateFlag, "RS", `ReSurvey site`)) %>%
  # Update column edit_site to label edits
  mutate(edit_site = if_else(UpdateFlag, TRUE, edit_site)) %>%
  # Remove unneeded column
  select(-UpdateFlag)

UA_0001

Text in Ilona’s email: One correction for issue4 – 6 coordinates are corrected – however this DB – has another problem with rs_codes (seems to me that original coding of plots are wrong – that´s why you have not repeating plots) and also one group of observation has also dubious coordinates. I am waiting for another corrections.

issue4_UA_0001 <- read_excel(
  here("data", "raw", "Data_corrections_Ilona", "Issue 4",
       "UA_0001_issue4_onlycorrected.xlsx"))
db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(UpdateFlag = PlotObservationID %in% 
           c(382299, 382301, 382325, 382314, 382322, 382324)) %>%
  # Join with issue4_UA_0001 based on PlotObservationID
  left_join(issue4_UA_0001 %>%
              mutate(PlotObservationID = PlotID) %>%
              select(PlotObservationID, LONGITUDE, LATITUDE)) %>%
  # Update Longitude and Latitude with the new values
  # if UpdateFlag = TRUE
  mutate(
    Longitude = if_else(UpdateFlag, LONGITUDE, Longitude),
    Latitude = if_else(UpdateFlag, LATITUDE, Latitude)
    ) %>%
  # Update ReSurvey site with "Syvulka_5" if UpdateFlag = TRUE
  mutate(`ReSurvey site` = if_else(UpdateFlag,
                                   "Syvulka_5", `ReSurvey site`)) %>%
  # Update column edit_coords to label edits
  mutate(edit_coords = if_else(UpdateFlag, TRUE, edit_coords)) %>%
  # Update column edit_site to label edits
  mutate(edit_site = if_else(UpdateFlag, TRUE, edit_site)) %>%
  # Remove unneeded columns
  select(-UpdateFlag, -LONGITUDE, -LATITUDE)
Joining with `by = join_by(PlotObservationID)`

Remove unneeded column

db_resurv_updated <- db_resurv_updated %>%
  # Remove "correct" column (not needed anymore)
  select(-correct)

Recalculate count resurveys

count_resurveys <- db_resurv_updated %>%
  # Convert dates to date format and get the year
  mutate(date = dmy(`Date of recording`), year = year(date)) %>%
 # Group by RS_CODE, `ReSurvey site`, `ReSurvey plot`
  group_by(RS_CODE, `ReSurvey site`, `ReSurvey plot`) %>%
  summarise(
    # Get how many different years for each unique group
    distinct_years=n_distinct(year), 
    # Get how many different dates for each unique group
    distinct_dates=n_distinct(date), .groups = "drop")

Summary stats:

summary(count_resurveys$distinct_years)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  1.000   2.000   2.000   3.469   4.000  55.000 
sd(count_resurveys$distinct_years)
[1] 2.678684

Histograms:

# For all data
ggplot(count_resurveys, aes(x = distinct_years)) + 
  geom_histogram(fill = "white", color = "black", bins = 55)+
  xlab("Number of ReSurvey observations (different years)") +
  ylab("Number of plots")

Number and proportion of plots with only 1 resurvey (should not be so!)

nrow(count_resurveys%>%filter(distinct_years==1))
[1] 147
nrow(count_resurveys%>%filter(distinct_years==1))/nrow(count_resurveys)
[1] 0.001224653

TO DO: Add rows “not_resurv” to EVA_db

TO DO: Implement choice of multiple observations per year based on Relevé area (m²)

Corrrection ISSUE 5

I did not correct anything as Ilona just sent a file confirming if (or not) part or all of each dataset contains presence / absence data. For us, the important thing is that if data is presence / absence, there is no EUNIS habitat assigned by the Expert System and thus we cannot use the data for relating to RS variables (except for the dataset “DK_Naturdata_Res” where I have used the Annex I codes provided by the custodian to assign an EUNIS habitat code).

Correction ISSUE 6

Correct coordinates for RS_CODE CZ_0029

Text in Ilona’s email: CZ_0029 I have noticed that some coordinates are wrongly placed so some of coordinates were corrected last year – I am sening you new coordinates for the whole dataset (again pleace mind that Long/lat is in DDMMSS.SS fromat not in decimal degree).

Attached: CZ_0029_correctedcoordinates.xlsx

issue6_CZ_0029 <- read_excel(
  here("data", "raw", "Data_corrections_Ilona", "Issue6",
       "CZ_0029_correctedcoordinates.xlsx"))

Apply the conversion function to the longitude and latitude columns:

issue6_CZ_0029 <- issue6_CZ_0029 %>%
  mutate(LONGITUDE_decimal = sapply(LONGITUDE, convert_to_decimal),
         LATITUDE_decimal = sapply(LATITUDE, convert_to_decimal))
nrow(issue6_CZ_0029)
[1] 180
nrow(db_resurv_updated %>%
       filter(RS_CODE == "CZ_0029"))
[1] 180

Update db_resurv_updated.

db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(UpdateFlag = RS_CODE == "CZ_0029") %>%
  # Join with issue6_CZ_0029 based on PlotObservationID
  left_join(issue6_CZ_0029 %>%
              select(PlotObservationID, LONGITUDE_decimal,
                     LATITUDE_decimal)) %>%
  # Update Longitude and Latitude and with the new values
  # if UpdateFlag = TRUE
  mutate(
    Longitude = if_else(UpdateFlag, LONGITUDE_decimal, Longitude),
    Latitude = if_else(UpdateFlag, LATITUDE_decimal, Latitude)
    ) %>%
  # Update column edit_coords to label edits
  mutate(edit_coords = if_else(UpdateFlag, TRUE, edit_coords)) %>%
  # Remove unneeded columns
  select(-UpdateFlag, -LONGITUDE_decimal, -LATITUDE_decimal) %>%
  # Recalculate updated coordinates
  mutate(Lon_updated = ifelse(is.na(Lon_prec), Longitude, Lon_prec),
         Lat_updated = ifelse(is.na(Lat_prec), Latitude, Lat_prec))
Joining with `by = join_by(PlotObservationID)`

Correct Country for some RS_CODEs

db_resurv_updated <- db_resurv_updated %>%
  # Create a column edit_country to mark rows to update
  mutate(edit_country = (RS_CODE == "CZ_0001" & Country == "Poland") |
           RS_CODE == "DE_0035" | RS_CODE == "forestREplot_EU_072") %>%
  # Update Country with new value if edit_country = TRUE
  mutate(
    Country = case_when(
      edit_country & RS_CODE == "CZ_0001" & Country == "Poland" ~ 
        "Slovak Republic",
      edit_country & RS_CODE == "DE_0035" ~ "Germany",
      edit_country & RS_CODE == "forestREplot_EU_072" ~ "Ukraine",
      TRUE ~ Country)
    )

AT_0004c

Longitude was wrong, correct.

db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(UpdateFlag = PlotObservationID == 4429 | PlotObservationID == 4519) %>%
  # Update Longitude with the new values
  # if UpdateFlag = TRUE
  mutate(Longitude = if_else(UpdateFlag, 13.681981, Longitude)) %>%
  # Update column edit_coords to label edits
  mutate(edit_coords = if_else(UpdateFlag, TRUE, edit_coords)) %>%
  # Remove unneeded columns
  select(-UpdateFlag)  %>%
  # Recalculate updated coordinates
  mutate(Lon_updated = ifelse(is.na(Lon_prec), Longitude, Lon_prec),
         Lat_updated = ifelse(is.na(Lat_prec), Latitude, Lat_prec))

CZ_0019_010

Latitude was wrong, correct.

db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(UpdateFlag = PlotObservationID == 6400 | PlotObservationID == 6401 |
           PlotObservationID == 6402) %>%
  # Update Latitude with the new values
  # if UpdateFlag = TRUE
  mutate(Latitude = if_else(UpdateFlag, 50.140555999999997, Latitude)) %>%
  # Update column edit_coords to label edits
  mutate(edit_coords = if_else(UpdateFlag, TRUE, edit_coords)) %>%
  # Remove unneeded columns
  select(-UpdateFlag)  %>%
  # Recalculate updated coordinates
  mutate(Lon_updated = ifelse(is.na(Lon_prec), Longitude, Lon_prec),
         Lat_updated = ifelse(is.na(Lat_prec), Latitude, Lat_prec))

IT_0008

Longitude and latitude were wrong, correct.

db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(UpdateFlag = PlotObservationID == 340426 |
           PlotObservationID == 340427) %>%
  # Update Longitude and Latitude with the new values
  # if UpdateFlag = TRUE
  mutate(Longitude = if_else(UpdateFlag, 14.84303305, Longitude),
         Latitude = if_else(UpdateFlag, 42.0966762, Latitude)) %>%
  # Update column edit_coords to label edits
  mutate(edit_coords = if_else(UpdateFlag, TRUE, edit_coords)) %>%
  # Remove unneeded columns
  select(-UpdateFlag)  %>%
  # Recalculate updated coordinates
  mutate(Lon_updated = ifelse(is.na(Lon_prec), Longitude, Lon_prec),
         Lat_updated = ifelse(is.na(Lat_prec), Latitude, Lat_prec))

AT_0007

issue6_AT_0007 <- read_excel(
  here("data", "raw", "Data_corrections_Ilona", "Issue6",
       "Issue6_newcorrections", "Issue6_AT_0007corrected.xlsx"))
db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(UpdateFlag = PlotObservationID %in% issue6_AT_0007$plotID) %>%
  # Join with issue6_AT_0007 based on PlotObservationID
  left_join(issue6_AT_0007 %>%
              mutate(PlotObservationID = plotID) %>%
              select(PlotObservationID, countrycode_new)) %>%
  # Update Country and with the new values
  # if UpdateFlag = TRUE
  mutate(Country = if_else(UpdateFlag, countrycode_new, Country)) %>%
  mutate(Country = case_when(
    Country == "AT" ~ "Austria",
    Country == "SI" ~ "Slovenia",
    Country == "CH" ~ "Switzerland",
    Country == "DE" ~ "Germany",
    Country == "IT" ~ "Italy",
    TRUE ~ Country)) %>%
  # Update column edit_country to label edits
  mutate(edit_country = if_else(UpdateFlag, TRUE, edit_country)) %>%
  # Remove unneeded columns
  select(-UpdateFlag, -countrycode_new)
Joining with `by = join_by(PlotObservationID)`

CH_0002

Text in Ilona’s email: CH_0002 corrected coordinates.

issue6_CH_0002 <- read_excel(
  here("data", "raw", "Data_corrections_Ilona", "Issue6",
       "Issue6_newcorrections", "Issue6_CH_0002_correctedcoordinates.xlsx"))
db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(UpdateFlag = PlotObservationID %in%
           issue6_CH_0002$PlotObservationID) %>%
  # Join with issue6_CH_0002 based on PlotObservationID
  left_join(issue6_CH_0002 %>%
              select(PlotObservationID, LONGITUDE, LATITUDE)) %>%
  # Update coordinates and with the new values
  # if UpdateFlag = TRUE
  mutate(Longitude = if_else(UpdateFlag, LONGITUDE, Longitude),
         Latitude = if_else(UpdateFlag, LATITUDE, Latitude)) %>%
  # Update column edit_coords to label edits
  mutate(edit_coords = if_else(UpdateFlag, TRUE, edit_coords)) %>%
  # Recalculate updated coordinates
  mutate(Lon_updated = ifelse(is.na(Lon_prec), Longitude, Lon_prec),
         Lat_updated = ifelse(is.na(Lat_prec), Latitude, Lat_prec)) %>%
  # Remove unneeded columns
  select(-UpdateFlag, -LONGITUDE, -LATITUDE)
Joining with `by = join_by(PlotObservationID)`

LT_0001

Text in Ilona’s email: LT_0001 – again corrected coordinates – all plots in LT.

issue6_LT_0001 <- read_excel(
  here("data", "raw", "Data_corrections_Ilona", "Issue6",
       "Issue6_newcorrections", "Issue6_LT_0001_correctedcoord.xlsx"))
db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(UpdateFlag = PlotObservationID %in%
           issue6_LT_0001$PlotObservationID) %>%
  # Join with issue6_LT_0001 based on PlotObservationID
  left_join(issue6_LT_0001 %>%
              select(PlotObservationID, Checked_Lat, Checked_Lon)) %>%
  # Update coordinates with the new values
  # if UpdateFlag = TRUE
  mutate(Longitude = if_else(UpdateFlag, Checked_Lon, Longitude),
         Latitude = if_else(UpdateFlag, Checked_Lat, Latitude)) %>%
  # Update column edit_coords to label edits
  mutate(edit_coords = if_else(UpdateFlag, TRUE, edit_coords)) %>%
  # Recalculate updated coordinates
  mutate(Lon_updated = ifelse(is.na(Lon_prec), Longitude, Lon_prec),
         Lat_updated = ifelse(is.na(Lat_prec), Latitude, Lat_prec)) %>%
  # Remove unneeded columns
  select(-UpdateFlag, -Checked_Lat, -Checked_Lon)
Joining with `by = join_by(PlotObservationID)`

DE_0037_074

Text in Ilona’s email: Please mind that precise coordinates are private. Ute gave me X and Y values with info about coordinates system - according her it is ETRS_1989_UTM_Zone_32N. According me x and y are in PD/83 / 3-degree Gauss-Kruger zone 3 (E-N) - EPSG:5666. SO I modified them into wgs84 (values in longitude/latitude) – I am not GIS guy so I just used ARCGIS Pro for projection – please check but the plots now are located on the correct side of river in Germany.

I took the cordinates as OK without further checking.

issue6_DE_0037_074 <- read_excel(
  here("data", "raw", "Data_corrections_Ilona", "Issue6",
       "Issue6_newcorrections", "Issue6_DE_0037_74_correctedfin.xlsx"))
db_resurv_updated <- db_resurv_updated %>%
  # Create an UpdateFlag to mark rows to update
  mutate(UpdateFlag = PlotObservationID %in%
           issue6_DE_0037_074$PlotObservationID) %>%
  # Join with issue6_DE_0037_074 based on PlotObservationID
  left_join(issue6_DE_0037_074 %>%
              mutate(Longitude_new = Longitude, Latitude_new = Latitude) %>%
              select(PlotObservationID, Longitude_new, Latitude_new)) %>%
  # Update coordinates with the new values
  # if UpdateFlag = TRUE
  mutate(Longitude = if_else(UpdateFlag, Longitude_new, Longitude),
         Latitude = if_else(UpdateFlag, Latitude_new, Latitude)) %>%
  # Update column edit_coords to label edits
  mutate(edit_coords = if_else(UpdateFlag, TRUE, edit_coords)) %>%
  # Recalculate updated coordinates
  mutate(Lon_updated = ifelse(is.na(Lon_prec), Longitude, Lon_prec),
         Lat_updated = ifelse(is.na(Lat_prec), Latitude, Lat_prec)) %>%
  # Remove unneeded columns
  select(-UpdateFlag, -Longitude_new, -Latitude_new)
Joining with `by = join_by(PlotObservationID)`

Check if Country is correct

Check if Country is correct directly in R (not ArcGIS).

# Load world boundaries
world <- ne_countries(scale = "medium", returnclass = "sf")

# Convert points to an sf object
points <- st_as_sf(db_resurv_updated %>%
                     filter(!is.na(Lon_updated) & !is.na(Lat_updated)),
                   coords = c("Lon_updated", "Lat_updated"), crs = 4326)

# Perform spatial join to find the country
points_with_country <- st_join(points, world, join = st_within, left = T)
# Compare countries
db_resurv_updated_country <- db_resurv_updated %>%
  left_join(points_with_country %>%
              select(PlotObservationID, sovereignt),
            by = "PlotObservationID") %>%
  mutate(geocoded_country = if_else(
    is.na(Lon_updated) | is.na(Lat_updated), 
    NA_character_, sovereignt)) %>%
  select(-sovereignt, - geometry) %>%
  # Change the names of some countries for matching
  mutate(geocoded_country = if_else(geocoded_country == "Czechia",
                                    "Czech Republic", geocoded_country)) %>%
  mutate(geocoded_country = if_else(geocoded_country == "Slovakia",
                                    "Slovak Republic", geocoded_country))
db_resurv_updated_country$country_correct <-
  db_resurv_updated_country$Country ==
  db_resurv_updated_country$geocoded_country
db_resurv_updated <- db_resurv_updated %>%
  left_join(db_resurv_updated_country %>%
              select(PlotObservationID, geocoded_country, country_correct))
Joining with `by = join_by(PlotObservationID)`
db_resurv_updated %>% count(country_correct)

Show map with points where country_correct is FALSE.

# Calculate the extent of the points
points_extent <- db_resurv_updated %>%
  filter(country_correct == FALSE) %>%
  summarise(
    lon_min = min(Lon_updated, na.rm = TRUE),
    lon_max = max(Lon_updated, na.rm = TRUE),
    lat_min = min(Lat_updated, na.rm = TRUE),
    lat_max = max(Lat_updated, na.rm = TRUE)
  )

# Add padding to the extent (adjust as needed)
padding <- 0.5  # Adjust padding to your preference
x_limits <- c(points_extent$lon_min - padding, points_extent$lon_max + padding)
y_limits <- c(points_extent$lat_min - padding, points_extent$lat_max + padding)

# Compute centroids for labeling
world_centroids <- world %>%
  mutate(centroid = st_centroid(geometry)) %>%
  mutate(lon = st_coordinates(centroid)[, 1],
         lat = st_coordinates(centroid)[, 2])

# Create the zoomed map
ggplot() +
  geom_sf(data = world, fill = "lightblue", color = "gray") +
  geom_point(data = db_resurv_updated %>%
               filter(country_correct == FALSE),
             aes(x = Lon_updated, y = Lat_updated, color = Country),
             size = 2) +
  geom_text(data = world_centroids, 
            aes(x = lon, y = lat, label = sovereignt),
            size = 3, color = "black") +  # Adjust size and color as needed
  coord_sf(xlim = x_limits, ylim = y_limits) +
  theme_minimal()

Show map with points where country_correct is NA.

# Calculate the extent of the points
points_extent <- db_resurv_updated %>%
  filter(is.na(country_correct)) %>%
  summarise(
    lon_min = min(Lon_updated, na.rm = TRUE),
    lon_max = max(Lon_updated, na.rm = TRUE),
    lat_min = min(Lat_updated, na.rm = TRUE),
    lat_max = max(Lat_updated, na.rm = TRUE)
  )

# Add padding to the extent (adjust as needed)
padding <- 0.5  # Adjust padding to your preference
x_limits <- c(points_extent$lon_min - padding, points_extent$lon_max + padding)
y_limits <- c(points_extent$lat_min - padding, points_extent$lat_max + padding)

# Create the zoomed map
ggplot() +
  geom_sf(data = world, fill = "lightblue", color = "gray") +
  geom_point(data = db_resurv_updated %>%
               filter(is.na(country_correct)),
             aes(x = Lon_updated, y = Lat_updated, color = Country),
             size = 2) +
  geom_text(data = world_centroids, 
            aes(x = lon, y = lat, label = sovereignt),
            size = 3, color = "black") +  # Adjust size and color as needed
  coord_sf(xlim = x_limits, ylim = y_limits) +
  theme_minimal()

Points where country_correct is NA and Country == “United Kingdom”.

# Calculate the extent of the points
points_extent <- db_resurv_updated %>%
  filter(is.na(country_correct) & Country == "United Kingdom") %>%
  summarise(
    lon_min = min(Lon_updated, na.rm = TRUE),
    lon_max = max(Lon_updated, na.rm = TRUE),
    lat_min = min(Lat_updated, na.rm = TRUE),
    lat_max = max(Lat_updated, na.rm = TRUE)
  )

# Add padding to the extent (adjust as needed)
padding <- 0.5  # Adjust padding to your preference
x_limits <- c(points_extent$lon_min - padding, points_extent$lon_max + padding)
y_limits <- c(points_extent$lat_min - padding, points_extent$lat_max + padding)

# Create the zoomed map
ggplot() +
  geom_sf(data = world, fill = "lightblue", color = "gray") +
  geom_point(data = db_resurv_updated %>%
               filter(is.na(country_correct) & Country == "United Kingdom"),
             aes(x = Lon_updated, y = Lat_updated, color = Country),
             size = 2) +
  geom_text(data = world_centroids, 
            aes(x = lon, y = lat, label = sovereignt),
            size = 3, color = "black") +  # Adjust size and color as needed
  coord_sf(xlim = x_limits, ylim = y_limits) +
  theme_minimal()

Points where country_correct is NA and Country == “Denmark”.

# Calculate the extent of the points
points_extent <- db_resurv_updated %>%
  filter(is.na(country_correct) & Country == "Denmark") %>%
  summarise(
    lon_min = min(Lon_updated, na.rm = TRUE),
    lon_max = max(Lon_updated, na.rm = TRUE),
    lat_min = min(Lat_updated, na.rm = TRUE),
    lat_max = max(Lat_updated, na.rm = TRUE)
  )

# Add padding to the extent (adjust as needed)
padding <- 0.5  # Adjust padding to your preference
x_limits <- c(points_extent$lon_min - padding, points_extent$lon_max + padding)
y_limits <- c(points_extent$lat_min - padding, points_extent$lat_max + padding)

# Create the zoomed map
ggplot() +
  geom_sf(data = world, fill = "lightblue", color = "gray") +
  geom_point(data = db_resurv_updated %>%
               filter(is.na(country_correct) & Country == "Denmark"),
             aes(x = Lon_updated, y = Lat_updated, color = Country),
             size = 2) +
  geom_text(data = world_centroids, 
            aes(x = lon, y = lat, label = sovereignt),
            size = 3, color = "black") +  # Adjust size and color as needed
  coord_sf(xlim = x_limits, ylim = y_limits) +
  theme_minimal()

Points where country_correct is NA and Country == “Italy”.

# Calculate the extent of the points
points_extent <- db_resurv_updated %>%
  filter(is.na(country_correct) & Country == "Italy") %>%
  summarise(
    lon_min = min(Lon_updated, na.rm = TRUE),
    lon_max = max(Lon_updated, na.rm = TRUE),
    lat_min = min(Lat_updated, na.rm = TRUE),
    lat_max = max(Lat_updated, na.rm = TRUE)
  )

# Add padding to the extent (adjust as needed)
padding <- 0.5  # Adjust padding to your preference
x_limits <- c(points_extent$lon_min - padding, points_extent$lon_max + padding)
y_limits <- c(points_extent$lat_min - padding, points_extent$lat_max + padding)

# Create the zoomed map
ggplot() +
  geom_sf(data = world, fill = "lightblue", color = "gray") +
  geom_point(data = db_resurv_updated %>%
               filter(is.na(country_correct) & Country == "Italy"),
             aes(x = Lon_updated, y = Lat_updated, color = Country),
             size = 2) +
  geom_text(data = world_centroids, 
            aes(x = lon, y = lat, label = sovereignt),
            size = 3, color = "black") +  # Adjust size and color as needed
  coord_sf(xlim = x_limits, ylim = y_limits) +
  theme_minimal()

Points where country_correct is NA and Country is other or NA.

# Calculate the extent of the points
points_extent <- db_resurv_updated %>%
  filter(is.na(country_correct) & (
    Country == "Germany" | Country == "Lithuania" | Country == "Norway" |
      Country == "Poland" | Country == "Sweden" | is.na(Country))) %>%
  summarise(
    lon_min = min(Lon_updated, na.rm = TRUE),
    lon_max = max(Lon_updated, na.rm = TRUE),
    lat_min = min(Lat_updated, na.rm = TRUE),
    lat_max = max(Lat_updated, na.rm = TRUE)
  )

# Add padding to the extent (adjust as needed)
padding <- 0.5  # Adjust padding to your preference
x_limits <- c(points_extent$lon_min - padding, points_extent$lon_max + padding)
y_limits <- c(points_extent$lat_min - padding, points_extent$lat_max + padding)

# Create the zoomed map
ggplot() +
  geom_sf(data = world, fill = "lightblue", color = "gray") +
  geom_point(data = db_resurv_updated %>%
               filter(is.na(country_correct) & (
                 Country == "Germany" | Country == "Lithuania" |
                   Country == "Norway" | Country == "Poland" |
                   Country == "Sweden" | is.na(Country))),
             aes(x = Lon_updated, y = Lat_updated, color = Country),
             size = 2) +
  geom_text(data = world_centroids, 
            aes(x = lon, y = lat, label = sovereignt),
            size = 3, color = "black") +  # Adjust size and color as needed
  coord_sf(xlim = x_limits, ylim = y_limits) +
  theme_minimal()

3 points where country_correct is NA and Country is Lithuania have no coordinates.

db_resurv_updated %>%
  filter(is.na(country_correct) & Country == "Lithuania") %>%
  select(Lon_updated, Lat_updated)

Add column country_new and update column country_correct

Column country_new shows the geocoded_country, or, for points where country_correct is NA and Country is United Kingdom, Denmark, Italy, Sweden, Germany, Norway and Poland, it shows the actual Country, which was correct according to maps.

db_resurv_updated <- db_resurv_updated %>%
  # Add column country_new
  # Firs, consider geocoded_country
  mutate(country_new = geocoded_country) %>%
  # For points where country_correct is NA  and Country is United Kingdom, 
  # Denmark, Italy, Sweden, Germany, Norway and Poland, Country is correct
  mutate(country_new = if_else(
    is.na(country_correct) & 
      (Country == "United Kingdom" | Country == "Denmark" | Country == "Italy" |
         Country == "Sweden" | Country == "Germany" | Country == "Norway") |
      Country == "Poland",
    Country, country_new)) %>%
  # For points where country_correct is NA and Country is NA, 
  # country_new should be Germany
  mutate(country_new = if_else(is.na(country_correct) & is.na(Country),
                                "Germany", country_new))

Update column country_correct, based on if country_new is equal to Country.

db_resurv_updated <- db_resurv_updated %>%
  # Update column country_correct (TRUE if country_new is equal from Country)
  mutate(country_correct = Country == country_new)
db_resurv_updated %>% count(country_correct)

Remove unneeded column

db_resurv_updated <- db_resurv_updated %>%
  select(-geocoded_country)

Correction ISSUE 8

Correct some EUNIS codes that are probably wrong:

db_resurv_updated <- db_resurv_updated %>%
  # Create a column edit_expert_sys to mark rows to update
  # Set edit_expert_sys to FALSE if `Expert System` is NA
  mutate(
    edit_expert_sys = if_else(is.na(`Expert System`),FALSE, 
                              str_detect(`Expert System`, 
                                         "N16M|T1CT|N15A"))) %>%
  # Update `Expert System`if edit_expert_sys = TRUE
  mutate(`Expert System` = if_else(
    edit_expert_sys, 
    # Apply string replacements using str_replace_all()
    str_replace_all(`Expert System`, 
                    c("N16M" = "N16",
                      "T1CT" = "T1C",
                      "N15A" = "N15")),`Expert System`))

Correction ISSUE 10

Translate codes from Location method to words.

db_resurv_updated <- db_resurv_updated %>%
  # Create a column edit_loc_method to mark rows to update
  mutate(edit_loc_method = `Location method` %in%
           c("04", "01", "06", "02", "08", "07", "05", "2", "03", "4")) %>%
  mutate(`Location method` = if_else(`Location method` == "2", "02",
                                     `Location method`)) %>%
  mutate(`Location method` = if_else(`Location method` == "4", "04",
                                     `Location method`)) %>%
  # Update `Location method`if edit_loc_method = TRUE
  mutate(`Location method` = if_else(
    edit_loc_method,
    # Apply string replacements using str_replace_all()
    str_replace_all(`Location method`, 
                    c("01" = "Permanently marked plot isolated (i.e. somewhere within the site)",
                      "02" = "Marked plot in a grid (i.e. with regularly spaced neighbor plots)",
                      "03" = "Location with differential GPS",
                      "04" = "Location with GPS",
                      "05" = "Location from accurate map",
                      "06" = "Location from a description",
                      "07" = "Other",
                      "08" = "Marked plot in a transect")
                    ),
    `Location method`))
ggplot(db_resurv_updated, aes(`Location method`)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "Location method") + coord_flip()

Correction ISSUE 11

Unify codes for ReSurvey project types.

db_resurv_updated <- db_resurv_updated %>%
  # Create a column edit_RS_PROJTYP to mark rows to update
  mutate(edit_RS_PROJTYP = RS_PROJTYP == "Resampling" |
           RS_PROJTYP == "Permanent (man)") %>%
  mutate(
    RS_PROJTYP = str_replace(RS_PROJTYP, "^Resampling$", "resampling"), 
    RS_PROJTYP = str_replace(RS_PROJTYP, 
                             "^Permanent \\(man\\)$", "permanent (man)")
    )
ggplot(db_resurv_updated, aes(RS_PROJTYP, fill=`Manipulate (y/n)`)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "Resurvey project type") + coord_flip() +
  theme(legend.position = "top")

Correction ISSUE 13

Recalculate the column precision_new to be 0 when Lon_prec and Lat_prec are NA, and 1 when Lon_prec and Lat_prec are not NA.

db_resurv_updated <- db_resurv_updated %>%
  # Create a new column edit_precision_new to mark rows to update
  # (those where precision_new is NA but Lon_prec and Lat_prec are not NA
  mutate(edit_precision_new = is.na(precision_new) &
           (!is.na(Lon_prec) & !is.na(Lat_prec))) %>%
  # Update precision_new
  mutate(precision_new = ifelse(is.na(Lon_prec) & is.na(Lat_prec), 0, 1))

More corrections TBD?

Create EUNIS columns

Clean info on Expert system column and separate it when there are several codes.

db_resurv_updated <- db_resurv_updated %>%
  mutate(
    # Clean 'Expert System' column by removing "!" and replacing "~" with NA
    `Expert System` = case_when(
      `Expert System` == "~" ~ NA_character_,  # Replace "~" with NA
      TRUE ~ str_replace_all(`Expert System`, "!", "")  # Remove "!"
    )
  ) %>%
  # Separate the values in 'Expert System' into multiple columns
  separate(
    `Expert System`,
    into = c("EUNISa", "EUNISb", "EUNISc", "EUNISd"),
    sep = ",",
    extra = "drop",  # Drop extra values if there are more than columns
    fill = "right",   # Fill missing values with NA for cases with fewer values
    remove = FALSE    # Keep the original 'Expert System' column
  )

Calculate how many different EUNIS codes have been assigned:

db_resurv_updated <- db_resurv_updated %>%
  mutate(
    # Count the number of non-NA values across the EUNIS columns
    n_EUNIS = rowSums(!is.na(select(., starts_with("EUNIS"))))
  )
ggplot(db_resurv_updated, aes(n_EUNIS)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "Number of differnt EUNIS codes assigned") + coord_flip()

ggplot(db_resurv_updated %>% filter(n_EUNIS > 0), aes(n_EUNIS)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "Number of differnt EUNIS codes assigned") + coord_flip()

Add columns for the different EUNIS levels:

db_resurv_updated <- db_resurv_updated %>%
  mutate(
    # EUNISa levels
    EUNISa_1 = substr(EUNISa, 1, ifelse(str_starts(EUNISa, "MA"), 2, 1)),
    EUNISa_2 = ifelse(
      nchar(EUNISa) >= ifelse(str_starts(EUNISa, "MA"), 3, 2), 
      substr(EUNISa, 1, ifelse(str_starts(EUNISa, "MA"), 3, 2)),
      NA_character_
    ),
    EUNISa_3 = ifelse(
      nchar(EUNISa) >= ifelse(str_starts(EUNISa, "MA"), 4, 3), 
      substr(EUNISa, 1, ifelse(str_starts(EUNISa, "MA"), 4, 3)),
      NA_character_
      ),
    EUNISa_4 = ifelse(
      nchar(EUNISa) >= ifelse(str_starts(EUNISa, "MA"), 5, 4), 
      substr(EUNISa, 1, ifelse(str_starts(EUNISa, "MA"), 5, 4)),
      NA_character_
    ),
    
    # EUNISb levels
    EUNISb_1 = substr(EUNISb, 1, ifelse(str_starts(EUNISb, "MA"), 2, 1)),
    EUNISb_2 = ifelse(
      nchar(EUNISb) >= ifelse(str_starts(EUNISb, "MA"), 3, 2), 
      substr(EUNISb, 1, ifelse(str_starts(EUNISb, "MA"), 3, 2)),
      NA_character_
    ),
    EUNISb_3 = ifelse(
      nchar(EUNISb) >= ifelse(str_starts(EUNISb, "MA"), 4, 3), 
      substr(EUNISb, 1, ifelse(str_starts(EUNISb, "MA"), 4, 3)),
      NA_character_
    ),
    EUNISb_4 = ifelse(
      nchar(EUNISb) >= ifelse(str_starts(EUNISb, "MA"), 5, 4), 
      substr(EUNISb, 1, ifelse(str_starts(EUNISb, "MA"), 5, 4)),
      NA_character_
    ),
    
    # EUNISc levels
    EUNISc_1 = substr(EUNISc, 1, ifelse(str_starts(EUNISc, "MA"), 2, 1)),
    EUNISc_2 = ifelse(
      nchar(EUNISc) >= ifelse(str_starts(EUNISc, "MA"), 3, 2), 
      substr(EUNISc, 1, ifelse(str_starts(EUNISc, "MA"), 3, 2)),
      NA_character_
    ),
    EUNISc_3 = ifelse(
      nchar(EUNISc) >= ifelse(str_starts(EUNISc, "MA"), 4, 3), 
      substr(EUNISc, 1, ifelse(str_starts(EUNISc, "MA"), 4, 3)),
      NA_character_
    ),
    EUNISc_4 = ifelse(
      nchar(EUNISc) >= ifelse(str_starts(EUNISc, "MA"), 5, 4), 
      substr(EUNISc, 1, ifelse(str_starts(EUNISc, "MA"), 5, 4)),
      NA_character_
    ),
    
    # EUNISd levels
    EUNISd_1 = substr(EUNISd, 1, ifelse(str_starts(EUNISc, "MA"), 2, 1)),
    EUNISd_2 = ifelse(
      nchar(EUNISd) >= ifelse(str_starts(EUNISd, "MA"), 3, 2), 
      substr(EUNISd, 1, ifelse(str_starts(EUNISd, "MA"), 3, 2)),
      NA_character_
    ),
    EUNISd_3 = ifelse(
      nchar(EUNISd) >= ifelse(str_starts(EUNISd, "MA"), 4, 3), 
      substr(EUNISd, 1, ifelse(str_starts(EUNISd, "MA"), 4, 3)),
      NA_character_
    ),
    EUNISd_4 = ifelse(
      nchar(EUNISd) >= ifelse(str_starts(EUNISd, "MA"), 5, 4), 
      substr(EUNISd, 1, ifelse(str_starts(EUNISd, "MA"), 5, 4)),
      NA_character_
    )
  )

Create new columns with descriptions for the level 1 codes:

db_resurv_updated <- db_resurv_updated %>%
  mutate(
    EUNISa_1_descr = case_when(
      EUNISa_1 == "V" ~ "Vegetated man-made habitats",
      EUNISa_1 == "U" ~ "Inland habitats with no or little soil",
      EUNISa_1 == "T" ~ "Forests and other wooded land",
      EUNISa_1 == "S" ~ "Heathlands, scrub and tundra",
      EUNISa_1 == "R" ~ "Grasslands",
      EUNISa_1 == "Q" ~ "Wetlands",
      EUNISa_1 == "P" ~ "Inland waters",
      EUNISa_1 == "N" ~ "Coastal habitats",
      EUNISa_1 == "MA" ~ "Marine habitats",
      TRUE ~ NA_character_
    ),
    EUNISb_1_descr = case_when(
      EUNISb_1 == "V" ~ "Vegetated man-made habitats",
      EUNISb_1 == "U" ~ "Inland habitats with no or little soil",
      EUNISb_1 == "T" ~ "Forests and other wooded land",
      EUNISb_1 == "S" ~ "Heathlands, scrub and tundra",
      EUNISb_1 == "R" ~ "Grasslands",
      EUNISb_1 == "Q" ~ "Wetlands",
      EUNISb_1 == "P" ~ "Inland waters",
      EUNISb_1 == "N" ~ "Coastal habitats",
      EUNISb_1 == "MA" ~ "Marine habitats",
      TRUE ~ NA_character_
    ),
    EUNISc_1_descr = case_when(
      EUNISc_1 == "V" ~ "Vegetated man-made habitats",
      EUNISc_1 == "U" ~ "Inland habitats with no or little soil",
      EUNISc_1 == "T" ~ "Forests and other wooded land",
      EUNISc_1 == "S" ~ "Heathlands, scrub and tundra",
      EUNISc_1 == "R" ~ "Grasslands",
      EUNISc_1 == "Q" ~ "Wetlands",
      EUNISc_1 == "P" ~ "Inland waters",
      EUNISc_1 == "N" ~ "Coastal habitats",
      EUNISc_1 == "MA" ~ "Marine habitats",
      TRUE ~ NA_character_
    ),
    EUNISd_1_descr = case_when(
      EUNISd_1 == "V" ~ "Vegetated man-made habitats",
      EUNISd_1 == "U" ~ "Inland habitats with no or little soil",
      EUNISd_1 == "T" ~ "Forests and other wooded land",
      EUNISd_1 == "S" ~ "Heathlands, scrub and tundra",
      EUNISd_1 == "R" ~ "Grasslands",
      EUNISd_1 == "Q" ~ "Wetlands",
      EUNISd_1 == "P" ~ "Inland waters",
      EUNISd_1 == "N" ~ "Coastal habitats",
      EUNISd_1 == "MA" ~ "Marine habitats",
      TRUE ~ NA_character_
    )
  )

Plot for EUNISa_1 (the first assigned EUNIS in cases of multiple assignations, level 1):

ggplot(db_resurv_updated, aes(EUNISa_1_descr)) +
  geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations", x = "EUNIS level 1") +
  coord_flip()

ggplot(db_resurv_updated %>% filter(!is.na(EUNISa_1_descr)), 
       aes(EUNISa_1_descr)) +
  geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations", x = "EUNIS level 1") + 
  coord_flip()

Correct topographic vars as numeric

Correct some values and set altitude, slope and aspect as numeric:

db_resurv_updated <- db_resurv_updated %>%
   # Create a new column edit_altitude to mark rows to update
  # Set edit_atitude to FALSE if Altitude is NA
  mutate(edit_atitude = if_else(is.na(Altitude), FALSE,
                                str_detect(Altitude, "-"))) %>%
  # Update Altitude if edit_altitude = TRUE
  mutate( Altitude = if_else(edit_atitude, 
                             # Some altitude values have a "-" after the number,
                             # convert to numeric after removing that
                             as.numeric(gsub("-", "", Altitude)),
                             Altitude))
db_resurv_updated <- db_resurv_updated %>%
  # Create a new column edit_slope to mark rows to update
  # Set edit_slope to FALSE if `Slope (°)` is NA
  mutate(edit_slope = if_else(is.na(`Slope (°)`), FALSE,
                              str_detect(`Slope (°)`,"_|-"))) %>%
  # Update `Slope (°)` if edit_slope = TRUE
  mutate(
    `Slope (°)` = if_else(edit_slope,
                          ifelse(`Slope (°)` == "_" | `Slope (°)` == "-",
                                 NA, `Slope (°)`),
                          `Slope (°)`)) %>%
  # Convert slope and aspect to numeric
  mutate(`Slope (°)` = as.numeric(`Slope (°)`),
         `Aspect (°)` = as.numeric(`Aspect (°)`))
Warning: There was 1 warning in `mutate()`.
ℹ In argument: `Slope (°) = as.numeric(`Slope (°)`)`.
Caused by warning:
! NAs introduced by coercion

Add columns date and year

db_resurv_updated <- db_resurv_updated %>%
  mutate(date = dmy(`Date of recording`), year = year(date))

EUNIS from info on HabitatID from DK

Based on information got from Jesper.

This is so far not flagged as a change with an “edit_” column because I did not modify any of the original columns on the database. See if we somehow flag the rows where EUNIS was obtained from this info later on.

Read the data sent by Jesper from DK

db_DK_J<-read_tsv(here("data", "raw",
                       "DK_Naturdata_Res_habitat_hab_codes_Jesper",
                  "DK_Naturdata_Res_habitat_hab_codes.txt"))
Rows: 158800 Columns: 9── Column specification ─────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (2): HABITAT, Dataset
dbl (7): PlotObservationID, RELEVE_NR, CIRC_ID, PLOT5_ID, PLOT15_ID, Access regime, HabitatID
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Add info on HabitatID to db_resurv_updated

db_resurv_updated <- db_resurv_updated %>%
  # Keeping all obs in db_resurv_updated but not all in db_DK_J
  left_join(db_DK_J %>% select(PlotObservationID, HabitatID))
Joining with `by = join_by(PlotObservationID)`

Change some Annex I habitat codes that were wrong

db_resurv_updated <- db_resurv_updated %>%
  mutate(HabitatID = as.character(HabitatID)) %>%
  mutate(HabitatID = ifelse(HabitatID == "9998", "91D0",
                            ifelse(HabitatID == "9999", "91E0", HabitatID)))

Add info on correspondences HabitatID (DK, Jesper) - EUNIS

Read correspondences file:

correspondences<-read_excel(here("data", "edited",
                                 "correspondence_HabitatID_DK.xlsx"))

Add info to db_resurv_updated:

db_resurv_updated <- db_resurv_updated %>%
  # Keeping all obs in db_resurv_updated but not all in db_DK_J
  left_join(correspondences %>% select(HabitatID, EUNIS))
Joining with `by = join_by(HabitatID)`

Correct NA values in EUNIS

db_resurv_updated <- db_resurv_updated %>%
  mutate(EUNIS = ifelse(EUNIS == "NA", NA, EUNIS))

Add info on EUNIS (DK) to EUNISa:

db_resurv_updated <- db_resurv_updated %>%
  mutate(EUNISa =
           # If EUNIS (DK) is available, add as EUNISa
           ifelse(!is.na(EUNIS), EUNIS, 
                  # Otherwise keep EUNISa
                  EUNISa),
         EUNIS_assignation = ifelse(!is.na(EUNIS), "Info from DK",
                                    ifelse(is.na(EUNISa), "Not possible",
                                           "Expert system"))) %>%
  # Remove column EUNIS (DK)
  select(-EUNIS)
ggplot(db_resurv_updated, aes(EUNIS_assignation)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "EUNIS assignation")

Update columns for EUNIS levels and descriptions

Update the columns for the different EUNISs levels:

db_resurv_updated <- db_resurv_updated %>%
  mutate(
    # EUNISa levels
    EUNISa_1 = substr(EUNISa, 1, ifelse(str_starts(EUNISa, "MA"), 2, 1)),
    EUNISa_2 = ifelse(
      nchar(EUNISa) >= ifelse(str_starts(EUNISa, "MA"), 3, 2), 
      substr(EUNISa, 1, ifelse(str_starts(EUNISa, "MA"), 3, 2)),
      NA_character_
    ),
    EUNISa_3 = ifelse(
      nchar(EUNISa) >= ifelse(str_starts(EUNISa, "MA"), 4, 3), 
      substr(EUNISa, 1, ifelse(str_starts(EUNISa, "MA"), 4, 3)),
      NA_character_
      ),
    EUNISa_4 = ifelse(
      nchar(EUNISa) >= ifelse(str_starts(EUNISa, "MA"), 5, 4), 
      substr(EUNISa, 1, ifelse(str_starts(EUNISa, "MA"), 5, 4)),
      NA_character_
    )
  ) %>%
  # Remove HabitatID column
  select(-HabitatID)

Update columns with descriptions for the level 1 codes:

db_resurv_updated <- db_resurv_updated %>%
  mutate(
    EUNISa_1_descr = case_when(
      EUNISa_1 == "V" ~ "Vegetated man-made habitats",
      EUNISa_1 == "U" ~ "Inland habitats with no or little soil",
      EUNISa_1 == "T" ~ "Forests and other wooded land",
      EUNISa_1 == "S" ~ "Heathlands, scrub and tundra",
      EUNISa_1 == "R" ~ "Grasslands",
      EUNISa_1 == "Q" ~ "Wetlands",
      EUNISa_1 == "P" ~ "Inland waters",
      EUNISa_1 == "N" ~ "Coastal habitats",
      EUNISa_1 == "MA" ~ "Marine habitats",
      TRUE ~ NA_character_
    )
  )

Update number of different EUNIS codes

Recalculate how many different EUNIS codes have been assigned:

db_resurv_updated <- db_resurv_updated %>%
  mutate(
    # Count the number of non-NA values across the EUNIS columns
    n_EUNIS = rowSums(!is.na(select(., EUNISa:EUNISd)))
  )
ggplot(db_resurv_updated, aes(n_EUNIS)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "Number of differnt EUNIS codes assigned") + coord_flip()

ggplot(db_resurv_updated %>% filter(n_EUNIS > 0), aes(n_EUNIS)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "Number of differnt EUNIS codes assigned") + coord_flip()

New plot for EUNISa_1 (the first assigned EUNIS in cases of multiple assignations, level 1):

ggplot(db_resurv_updated, aes(EUNISa_1_descr)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "EUNIS level 1") + coord_flip()

ggplot(db_resurv_updated %>% filter(!is.na(EUNISa_1_descr)),
       aes(EUNISa_1_descr)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "EUNIS level 1") + coord_flip()

Add info on descriptions for EUNIS levels 2-4

descriptions<-read_excel(here("data", "edited",
                                 "EUNIS-Habitats-2021-06-01_modified.xlsx"))
# Define the columns and corresponding description column names
eunis_cols <- c("EUNISa_2", "EUNISa_3", "EUNISa_4",
                "EUNISb_2", "EUNISb_3", "EUNISb_4", 
                "EUNISc_2", "EUNISc_3", "EUNISc_4",
                "EUNISd_2", "EUNISd_3", "EUNISd_4")

# Create corresponding description column names
descr_col_names <- paste0(eunis_cols, "_descr")

# Use reduce to loop through the columns and join dynamically based on level
db_resurv_updated <- reduce(seq_along(eunis_cols), function(data, i) {
  # Extract level number from the column name (e.g., EUNISa_2 -> 2)
  level <- as.numeric(gsub("\\D", "", eunis_cols[i]))
  
  # Filter descriptions for the corresponding level
  descriptions_level <- descriptions %>%
    filter(level == level) %>%
    select(`EUNIS 2020 code`, `EUNIS-2021 habitat name`)
  
  # Perform the left_join and rename the column dynamically
  data %>%
    left_join(
      descriptions_level,
      by = setNames("EUNIS 2020 code", eunis_cols[i])
    ) %>%
    rename(!!descr_col_names[i] := `EUNIS-2021 habitat name`)
}, .init = db_resurv_updated)

The matching did not work sometimes, correct!

# Correct EUNISa levels 2-4 descriptions
db_resurv_updated <- db_resurv_updated %>%
  mutate(EUNISa_2_descr = 
           ifelse(!is.na(EUNISa_2_descr), EUNISa_2_descr,
                  case_when(
                    EUNISa_2 == "Pf" ~ "Fresh-water submerged vegetation",
                    EUNISa_2 == "Pj" ~ "Stonewort vegetation",
                    EUNISa_2 == "R4" ~ "Alpine and subalpine grasslands",
                    EUNISa_2 == "Pb" ~ "Calcareous spring and spring brook",
                    EUNISa_2 == "Qb" ~ "Wetlands",
                    EUNISa_2 == "R3" ~ "Seasonally wet and wet grasslands",
                    EUNISa_2 == "Qa" ~ "Mires",
                    EUNISa_2 == "Pa" ~ "Base-poor spring and spring brook",
                    EUNISa_2 == "Ph" ~ "Oligotrophic-water vegetation",
                    EUNISa_2 == "Pg" ~ "Fresh-water nymphaeid vegetation",
                    EUNISa_2 ==
                      "Pd" ~ "Fresh-water small pleustophyte vegetation",
                    EUNISa_2 == "Pc" ~ "Brackish-water vegetation",
                    EUNISa_2 ==
                      "Pe" ~ "Fresh-water large pleustophyte vegetation",
                    EUNISa_2 == "Pi" ~ "Dystrophic-water vegetation",
                    EUNISa_2 == "S1" ~ "Tundra",
                    EUNISa_2 ==
                      "U7" ~ "Unvegetated or sparsely vegetated gravel bars",
                    EUNISa_2 == "Q6" ~ "Periodically exposed shores",
                    TRUE ~ NA_character_)
                  ),
         EUNISa_3_descr = 
           ifelse(!is.na(EUNISa_3_descr), EUNISa_3_descr,
                  case_when(
                    EUNISa_3 =="U71" ~ "Unvegetated or sparsely vegetated gravel bar in montane and alpine regions",
                    EUNISa_3 =="Q61" ~ "Periodically exposed shore with stable, eutrophic sediments with pioneer or ephemeral vegetation",
                    EUNISa_3 =="Q62" ~ "Periodically exposed shore with stable, mesotrophic sediments with pioneer or ephemeral vegetation",
                    TRUE ~ NA_character_
                    ))
         )
# Correct EUNISb levels 2-4 descriptions
db_resurv_updated <- db_resurv_updated %>%
  mutate(EUNISb_2_descr = 
           ifelse(!is.na(EUNISb_2_descr), EUNISb_2_descr,
                  case_when(
                    EUNISb_2 == "Pj" ~ "Stonewort vegetation",
                    EUNISb_2 == "R4" ~ "Alpine and subalpine grasslands",
                    EUNISb_2 == "Pf" ~ "Fresh-water submerged vegetation",
                    TRUE ~ NA_character_)
                  )
         )

EUNISc and EUNISd levels 2-4 are OK.

Notes EUNIS codes - to change?

https://www.sci.muni.cz/botany/chytry/Schaminee_etal2021_EEA-Report-Aquatic-Wetland-habitats.pdf

EUNISa_2 == “Q6” : “Periodically exposed shores” EUNISa_3 = “Q61” : “Periodically exposed shore with stable, eutrophic sediments with pioneer or ephemeral vegetation” EUNISa_3 == “Q62” : “Periodically exposed shore with stable, mesotrophic sediments with pioneer or ephemeral vegetation”

This classification of Q + numbers is now coexisting in the database with Qa & Qb (metadata). How to proceed?

db_resurv_updated %>% filter(EUNISa_1 == "Q") %>% distinct(EUNISa_2)

Plots of level-2 categories within each level 1 category

ggplot(db_resurv_updated %>% filter(EUNISa_1 == "MA"), aes(EUNISa_2_descr)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "EUNIS level 2") + coord_flip() +
  ggtitle(
    db_resurv_updated %>% filter(EUNISa_1 == "MA") %>% distinct(EUNISa_1_descr))
ggsave(filename=here("output", "figures","MA_level2.tiff"),
       width=14,height=8,units="cm",dpi=300)

ggplot(db_resurv_updated %>% filter(EUNISa_1 == "N"), aes(EUNISa_2_descr)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "EUNIS level 2") + coord_flip() +
  ggtitle(
    db_resurv_updated %>% filter(EUNISa_1 == "N") %>% distinct(EUNISa_1_descr))
ggsave(filename=here("output", "figures","N_level2.tiff"),
       width=14,height=8,units="cm",dpi=300)

ggplot(db_resurv_updated %>% filter(EUNISa_1 == "P"), aes(EUNISa_2_descr)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "EUNIS level 2") + coord_flip() +
  ggtitle(
    db_resurv_updated %>% filter(EUNISa_1 == "P") %>% distinct(EUNISa_1_descr))
ggsave(filename=here("output", "figures","P_level2.tiff"),
       width=14,height=8,units="cm",dpi=300)

ggplot(db_resurv_updated %>% filter(EUNISa_1 == "Q"), aes(EUNISa_2_descr)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "EUNIS level 2") + coord_flip() +
  ggtitle(
    db_resurv_updated %>% filter(EUNISa_1 == "Q") %>% distinct(EUNISa_1_descr))
ggsave(filename=here("output", "figures","Q_level2.tiff"),
       width=14,height=8,units="cm",dpi=300)

ggplot(db_resurv_updated %>% filter(EUNISa_1 == "R"), aes(EUNISa_2_descr)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "EUNIS level 2") + coord_flip() +
  ggtitle(
    db_resurv_updated %>% filter(EUNISa_1 == "R") %>% distinct(EUNISa_1_descr))
ggsave(filename=here("output", "figures","R_level2.tiff"),
       width=14,height=8,units="cm",dpi=300)

ggplot(db_resurv_updated %>% filter(EUNISa_1 == "S"), aes(EUNISa_2_descr)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "EUNIS level 2") + coord_flip() +
  ggtitle(
    db_resurv_updated %>% filter(EUNISa_1 == "S") %>% distinct(EUNISa_1_descr))
ggsave(filename=here("output", "figures","S_level2.tiff"),
       width=16,height=8,units="cm",dpi=300)

ggplot(db_resurv_updated %>% filter(EUNISa_1 == "T"), aes(EUNISa_2_descr)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "EUNIS level 2") + coord_flip() +
  ggtitle(
    db_resurv_updated %>% filter(EUNISa_1 == "T") %>% distinct(EUNISa_1_descr))
ggsave(filename=here("output", "figures","T_level2.tiff"),
       width=14,height=8,units="cm",dpi=300)

ggplot(db_resurv_updated %>% filter(EUNISa_1 == "U"), aes(EUNISa_2_descr)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "EUNIS level 2") + coord_flip() +
  ggtitle(
    db_resurv_updated %>% filter(EUNISa_1 == "U") %>% distinct(EUNISa_1_descr))
ggsave(filename=here("output", "figures","U_level2.tiff"),
       width=16,height=8,units="cm",dpi=300)

ggplot(db_resurv_updated %>% filter(EUNISa_1 == "V"), aes(EUNISa_2_descr)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "EUNIS level 2") + coord_flip() +
  ggtitle(
    db_resurv_updated %>% filter(EUNISa_1 == "V") %>% distinct(EUNISa_1_descr))
ggsave(filename=here("output", "figures","V_level2.tiff"),
       width=14,height=8,units="cm",dpi=300)

Save to clean data

Save so-far updated and clean datafile for resurvey database:

write_tsv(db_resurv_updated,here("data", "clean","db_resurv_updated_clean.csv"))

Session info

sessionInfo()
R version 4.4.2 (2024-10-31 ucrt)
Platform: x86_64-w64-mingw32/x64
Running under: Windows 11 x64 (build 22631)

Matrix products: default


locale:
[1] LC_COLLATE=English_United States.utf8  LC_CTYPE=English_United States.utf8   
[3] LC_MONETARY=English_United States.utf8 LC_NUMERIC=C                          
[5] LC_TIME=English_United States.utf8    

time zone: Europe/Madrid
tzcode source: internal

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] rnaturalearth_1.0.1 sf_1.0-19           forcats_1.0.0       tibble_3.2.1       
 [5] tidyverse_2.0.0     readxl_1.4.3        purrr_1.0.2         stringr_1.5.1      
 [9] ggplot2_3.5.1       lubridate_1.9.4     dplyr_1.1.4         tidyr_1.3.1        
[13] readr_2.1.5         here_1.0.1         

loaded via a namespace (and not attached):
 [1] gtable_0.3.6            xfun_0.49               bslib_0.8.0            
 [4] tzdb_0.4.0              vctrs_0.6.5             tools_4.4.2            
 [7] generics_0.1.3          parallel_4.4.2          proxy_0.4-27           
[10] fansi_1.0.6             pkgconfig_2.0.3         KernSmooth_2.23-24     
[13] lifecycle_1.0.4         compiler_4.4.2          farver_2.1.2           
[16] textshaping_0.4.1       munsell_0.5.1           terra_1.8-15           
[19] codetools_0.2-20        sass_0.4.9              htmltools_0.5.8.1      
[22] class_7.3-22            yaml_2.3.10             pillar_1.9.0           
[25] crayon_1.5.3            jquerylib_0.1.4         classInt_0.4-11        
[28] cachem_1.1.0            wk_0.9.4                rnaturalearthdata_1.0.0
[31] tidyselect_1.2.1        digest_0.6.37           stringi_1.8.4          
[34] labeling_0.4.3          rprojroot_2.0.4         fastmap_1.2.0          
[37] grid_4.4.2              colorspace_2.1-1        cli_3.6.3              
[40] magrittr_2.0.3          utf8_1.2.4              e1071_1.7-16           
[43] withr_3.0.2             scales_1.3.0            bit64_4.5.2            
[46] timechange_0.3.0        rmarkdown_2.29          httr_1.4.7             
[49] bit_4.5.0.1             cellranger_1.1.0        ragg_1.3.3             
[52] hms_1.1.3               evaluate_1.0.1          knitr_1.49             
[55] s2_1.1.7                rlang_1.1.4             Rcpp_1.0.13-1          
[58] glue_1.8.0              DBI_1.2.3               rstudioapi_0.17.1      
[61] vroom_1.6.5             jsonlite_1.8.9          R6_2.5.1               
[64] systemfonts_1.1.0       units_0.8-5            
LS0tDQp0aXRsZTogIlNjcmlwdCB0byBpbmNvcnBvcmF0ZSBjb3JyZWN0aW9ucyBzZW50IGJ5IElsb25hIG9uIHRoZSBSZVN1cnZleSBkYXRhYmFzZSINCmF1dGhvcjogIkFsaWNpYSBWYWxkw6lzIg0KZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIgJVknKWAiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIExvYWQgbGlicmFyaWVzDQoNCmBgYHtyfQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGhlcmUpDQpsaWJyYXJ5KHJlYWR4bCkNCmxpYnJhcnkoc3RyaW5ncikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShzZikNCmxpYnJhcnkocm5hdHVyYWxlYXJ0aCkNCmBgYA0KDQojIFJlYWQgdGhlIGRhdGENCg0KYGBge3J9DQpkYl9yZXN1cnY8LXJlYWRfdHN2KGhlcmUoImRhdGEiLCAiZWRpdGVkIiwgImRiX3Jlc3Vydi5jc3YiKSkNCmBgYA0KDQojIFByb2JsZW1zIChkbyBub3QgYWZmZWN0IHVzKQ0KDQpgYGB7cn0NCnByb2JsZW1zPC1wcm9ibGVtcyhkYl9yZXN1cnYpDQpzb3J0KHVuaXF1ZShwcm9ibGVtcyRjb2wpKQ0KbmFtZXMoZGJfcmVzdXJ2W2MoNywxMyldKQ0KYGBgDQoNCk5vIHByb2JsZW1zIHJlYWxseSENCg0KIyBVcGRhdGUgY29vcmRpbmF0ZXMNCg0KQ3JlYXRlIG5ldyBjb2x1bW4gd2l0aCBvbGQgY29vcmRpbmF0ZXMgaWYgbmV3IG5vdCBhdmFpbGFibGUsIGFuZCB3aXRoIG5ldyBpZiBhdmFpbGFibGUuDQoNCmBgYHtyfQ0KZGJfcmVzdXJ2IDwtIGRiX3Jlc3VydiAlPiUNCiAgbXV0YXRlKExvbl91cGRhdGVkID0gaWZlbHNlKGlzLm5hKExvbl9wcmVjKSwgTG9uZ2l0dWRlLCBMb25fcHJlYyksDQogICAgICAgICBMYXRfdXBkYXRlZCA9IGlmZWxzZShpcy5uYShMYXRfcHJlYyksIExhdGl0dWRlLCBMYXRfcHJlYykpDQpgYGANCg0KYGBge3J9DQpwcmludChkYl9yZXN1cnYsIHdpZHRoID0gSW5mKQ0KYGBgDQoNCg0KIyBCYXJwbG90IG9mIGNvb3JkaW5hdGUgc3RhdHVzDQoNCmBgYHtyfQ0KIyBEZWZpbmUgYSB0aHJlc2hvbGQgKGUuZy4sIDAuMDAxIGRlZ3JlZXMgZm9yIGxvbmdpdHVkZS9sYXRpdHVkZSBkaWZmZXJlbmNlcykNCnRocmVzaG9sZCA8LSAwLjAwMQ0KZGJfcmVzdXJ2ICU+JSANCiAgZ3JvdXBfYnkoUlNfQ09ERSwgYFJlU3VydmV5IHNpdGVgLCBgUmVTdXJ2ZXkgcGxvdGApICU+JQ0KICBtdXRhdGUoDQogICAgbG9uX3JhbmdlID0gaWZlbHNlKGFsbChpcy5uYShMb25fdXBkYXRlZCkpLCBOQSwNCiAgICAgICAgICAgICAgICAgICAgICAgIG1heChMb25fdXBkYXRlZCwgbmEucm0gPSBUKSAtIA0KICAgICAgICAgICAgICAgICAgICAgICAgIG1pbihMb25fdXBkYXRlZCwgbmEucm0gPSBUKSksDQogICAgbGF0X3JhbmdlID0gaWZlbHNlKGFsbChpcy5uYShMYXRfdXBkYXRlZCkpLCBOQSwNCiAgICAgICAgICAgICAgICAgICAgICAgIG1heChMYXRfdXBkYXRlZCwgbmEucm0gPSBUKSAtIA0KICAgICAgICAgICAgICAgICAgICAgICAgIG1pbihMYXRfdXBkYXRlZCwgbmEucm0gPSBUKSksDQogICAgY29vcmRpbmF0ZXNfZXF1YWwgPSBpZmVsc2UoaXMubmEoTG9uX3VwZGF0ZWQpICYgaXMubmEoTGF0X3VwZGF0ZWQpLCBOQSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb25fcmFuZ2UgPT0gMCAmIGxhdF9yYW5nZSA9PSAwKSwNCiAgICBjb29yZGluYXRlc19jb25zaXN0ZW50ID0gaWZlbHNlKGlzLm5hKExvbl91cGRhdGVkKSAmIGlzLm5hKExhdF91cGRhdGVkKSwgTkEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb25fcmFuZ2UgPCB0aHJlc2hvbGQgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF0X3JhbmdlIDwgdGhyZXNob2xkKQ0KICApICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIGdyb3VwX2J5KFJTX0NPREUsYFJlU3VydmV5IHNpdGVgLCBgUmVTdXJ2ZXkgcGxvdGApICU+JQ0KICBzdW1tYXJpemUoaXNfZXF1YWwgPSBhbGwoY29vcmRpbmF0ZXNfZXF1YWwpLA0KICAgICAgICAgICAgaXNfY29uc2lzdGVudCA9IGFsbChjb29yZGluYXRlc19jb25zaXN0ZW50KSwNCiAgICAgICAgICAgIC5ncm91cHMgPSAiZHJvcCIpICU+JQ0KICBtdXRhdGUoY29vcmRpbmF0ZV9zdGF0dXMgPSBjYXNlX3doZW4oDQogICAgaXNfZXF1YWwgfiAiRXF1YWwiLA0KICAgICFpc19lcXVhbCAmIGlzX2NvbnNpc3RlbnQgfiAiQ29uc2lzdGVudCAoPCAwLjAwMcK6KSIsDQogICAgIWlzX2VxdWFsICYgIWlzX2NvbnNpc3RlbnQgfiAiSW5jb25zaXN0ZW50ICg+IDAuMDAxwropIikpICU+JQ0KICBjb3VudChjb29yZGluYXRlX3N0YXR1cyklPiUNCiAgbXV0YXRlKHBlcmNlbnRhZ2UgPSBuIC8gc3VtKG4pICogMTAwKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gcGVyY2VudGFnZSwgeSA9IGNvb3JkaW5hdGVfc3RhdHVzLCBmaWxsID0gY29vcmRpbmF0ZV9zdGF0dXMpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArIA0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGFzdGUwKHJvdW5kKHBlcmNlbnRhZ2UsIDEpLCAiJSIpKSwNCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLCBzaXplID0gMykgKyANCiAgbGFicyh4ID0gIlBlcmNlbnRhZ2Ugb2YgUGxvdHMiLCB5ID0gTlVMTCkgKw0KICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpKSArDQogIGNvb3JkX2ZsaXAoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQojIENvcnJlY3Rpb24gSVNTVUUgMQ0KDQpUZXh0IGluIElsb25hJ3MgZW1haWw6IA0KSXNzdWUgMTogYWxyZWFkeSBjb3JyZWN0ZWQgaW4gdGhlIHBhc3QgYW5kIGZvciBDSF8wMDAyIHJzX3Bsb3RzIGZpbGxlZC4NCg0KQXR0YWNoZWQ6IENIXzAwMDJfaXNzdWUxLnR4dA0KDQpgYGB7cn0NCmNvcnJlY3Rpb24xPC1yZWFkX3RzdihoZXJlKCJkYXRhIiwgInJhdyIsICJEYXRhX2NvcnJlY3Rpb25zX0lsb25hIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJDSF8wMDAyX2lzc3VlMS50eHQiKSkNCmBgYA0KDQpOdW1iZXIgb2Ygcm93cyBpbiBjb3JyZWN0aW9uMSA9IG51bWJlciBvZiByb3dzIGluIGRiIHdpdGggdGhhdCBSU19DT0RFOg0KDQpgYGB7cn0NCm5yb3coY29ycmVjdGlvbjEpDQpucm93KGRiX3Jlc3VydiAlPiUgZmlsdGVyKFJTX0NPREUgPT0gIkNIXzAwMDIiKSkNCmBgYA0KDQpudW1iZXIgb2Ygcm93cyBpbiBkYiB3aXRoIHRoYXQgUlNfQ09ERSBhbmQgcGxvdCBhcyBOQSA9IDExMS4NCk51bWJlciBvZiByb3dzIGluIGNvcnJlY3Rpb24xIHdpdGggcGxvdCBhcyBOQSA9IDAuDQoNCmBgYHtyfQ0KbnJvdyhkYl9yZXN1cnYgJT4lIGZpbHRlcihSU19DT0RFID09ICJDSF8wMDAyIiAmIGlzLm5hKGBSZVN1cnZleSBwbG90YCkpKQ0KbnJvdyhjb3JyZWN0aW9uMSAlPiUgZmlsdGVyKGlzLm5hKFJTX1BMT1QpKSkNCmBgYA0KDQpOYW1lcyBpbiBjb3JyZWN0aW9uMSBhcmUgZGlmZmVyZW50IGZyb20gZGI6DQoNCmBgYHtyfQ0KbmFtZXMoZGJfcmVzdXJ2KQ0KbmFtZXMoY29ycmVjdGlvbjEpDQpgYGANCg0KUmVuYW1lIGNvbHVtbnMgaW4gY29ycmVjdGlvbjEgdG8gaGF2ZSB0aGUgc2FtZSBuYW1lcyBhcyBpbiBkYl9yZXN1cnYuDQoNCmBgYHtyfQ0KY29ycmVjdGlvbjEgPC0gY29ycmVjdGlvbjEgJT4lDQogIHJlbmFtZShgUmVTdXJ2ZXkgcGxvdCAoWS9OKWAgPSBSRVNVUlZFWSwNCiAgICAgICAgIGBSZVN1cnZleSBwcm9qZWN0YCA9IFJTX1BST0pFQ1QsDQogICAgICAgICBgUmVTdXJ2ZXkgc2l0ZWAgPSBSU19TSVRFLA0KICAgICAgICAgYFJlU3VydmV5IHBsb3RgID0gUlNfUExPVCwNCiAgICAgICAgIGBSZVN1cnZleSBvYnNlcnZhdGlvbmAgPSBSU19PQlNFUlYsDQogICAgICAgICBgTG9jYXRpb24gbWV0aG9kYCA9IExPQ19NRVRIT0QpICU+JQ0KICBtdXRhdGUoUlNfRFVQTCA9IGFzLmNoYXJhY3RlcihSU19EVVBMKSkNCmBgYA0KDQpVcGRhdGUgZGJfcmVzdXJ2IG9ubHkgZm9yIGNhc2VzIHdoZXJlIFJlU3VydmV5IHBsb3QgaXMgTkEuDQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X3VwZGF0ZWQgPC0gZGJfcmVzdXJ2ICU+JQ0KICAjIENyZWF0ZSBhIGNvbHVtbiBlZGl0X3Bsb3QgdG8gbWFyayByb3dzIHRvIHVwZGF0ZQ0KICBtdXRhdGUoZWRpdF9wbG90ID0gaXMubmEoYFJlU3VydmV5IHBsb3RgKSkgJT4lDQogICMgSm9pbiB3aXRoIGNvcnJlY3Rpb24xIGJhc2VkIG9uIFBsb3RPYnNlcnZhdGlvbklEDQogICMgUmVuYW1lIGNvbHVtbiBgUmVzdXJ2ZXkgcGxvdGAgdG8gYXZvaWQgam9pbmluZyBvbiB0aGlzIGNvbHVtbg0KICBsZWZ0X2pvaW4oY29ycmVjdGlvbjEgJT4lIHNlbGVjdChQbG90T2JzZXJ2YXRpb25JRCwgYFJlU3VydmV5IHBsb3RgKSAlPiUNCiAgICAgICAgICAgICAgcmVuYW1lKGBSZVN1cnZleSBwbG90Lm5ld2AgPSBgUmVTdXJ2ZXkgcGxvdGApKSAlPiUNCiAgIyBVcGRhdGUgYFJlU3VydmV5IHBsb3RgIHdpdGggdGhlIG5ldyB2YWx1ZXMgaWYgZWRpdF9wbG90ID0gVFJVRQ0KICBtdXRhdGUoYFJlU3VydmV5IHBsb3RgID0gaWZfZWxzZShlZGl0X3Bsb3QsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgUmVTdXJ2ZXkgcGxvdC5uZXdgLCBgUmVTdXJ2ZXkgcGxvdGApKSAlPiUNCiAgIyBSZW1vdmUgdW5uZWVkZWQgY29sdW1uDQogIHNlbGVjdCgtYFJlU3VydmV5IHBsb3QubmV3YCkNCmBgYA0KDQpDaGVjayB0aGF0IHRoZXJlIGFyZSBubyByb3dzIHdpdGggUmVTdXJ2ZXkgcGxvdCBhcyBOQS4NCg0KYGBge3J9DQpucm93KGRiX3Jlc3Vydl91cGRhdGVkICU+JSBmaWx0ZXIoaXMubmEoYFJlU3VydmV5IHBsb3RgKSkpDQpgYGANCg0KQ2hlY2sgdGhhdCB0aGVyZSBhcmUgMTExIHJvd3Mgd2hlcmUgZWRpdF9wbG90IGlzIFRSVUUuDQoNCmBgYHtyfQ0KbnJvdyhkYl9yZXN1cnZfdXBkYXRlZCAlPiUgZmlsdGVyKGVkaXRfcGxvdCkpDQpgYGANCg0KIyBDb3JyZWN0aW9uIElTU1VFIDINCg0KVGV4dCBpbiBJbG9uYSdzIGVtYWlsOiANCklzc3VlIDI6IEkgaGF2ZSBwcmVwYXJlZCByZW1hcmtzIGZvciBkYXRhc2V0cyAoZm9yIG1hbnkgc2VuZCBlLW1haWwgdG8gY3VzdG9kaWFuKS4gSVRfMDAwOCBhbmQgREVfMDAzMSBjb3JyZWN0ZWQNCg0KQXR0YWNoZWQ6ICIyMDBfSXNzdWUyX2RhdGFzZXRzLnhsc3giLCAiREVfMDAzMV9jb29yZGluYXRlc2ZpbGxlZC54bHN4IiwgIklUXzAwMDhfY29ycmVjdGVkLnhsc3giLg0KDQpJbiAiMjAwX0lzc3VlMl9kYXRhc2V0cy54bHN4IiB0aGVyZSBpcyBhIGxpc3Qgb2YgZGF0YXNldHMgd2l0aCBjb29yZGluYXRlcyBtaXNzaW5nLiBGb3IgdHdvIG9mIHRoZW0gKGEgYW5kIGIgYmVsb3cpLCBjb29yZGluYXRlcyBhcmUgYWRkZWQuIEZvciBvdGhlcnMsIElsb25hIHNlbnQgYW4gZW1haWwgdG8gb3duZXIuIEZvciBvdGhlcnMsIHRoZXJlIGlzIGEgcmVtYXJrIGJ1dCBub3Qgc3VyZSBpZiBzaGUgc2VudCBlbWFpbCB0byBvd25lciAod2FpdCkuDQoNCkkgd2lsbCBvbmx5IHVwZGF0ZSBlZGl0c19BViBpbiB0aGUgY2FzZXMgd2hlcmUgY29vcmRpbmF0ZXMgYXJlIGFkZGVkLg0KDQojIyBhDQoNCmBgYHtyfQ0KY29ycmVjdGlvbjJhIDwtIHJlYWRfZXhjZWwoDQogIGhlcmUoImRhdGEiLCAicmF3IiwgIkRhdGFfY29ycmVjdGlvbnNfSWxvbmEiLCAiSXNzdWUyIiwNCiAgICAgICAiSVRfMDAwOF9jb3JyZWN0ZWQueGxzeCIpKQ0KYGBgDQoNCmBgYHtyfQ0KcHJpbnQoY29ycmVjdGlvbjJhLCB3aWR0aCA9IEluZikNCmBgYA0KDQpOZWVkIHRvIHVwZGF0ZSBMb25naXR1ZGUsIExhdGl0dWRlIGFuZCBMb2NhdGlvbiB1bmNlcnRhaW50eSAobSkuDQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X3VwZGF0ZWQgPC0gZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogICMgQ3JlYXRlIGEgY29sdW1uIGVkaXRfY29vcmRzX3VuYyB0byBtYXJrIHJvd3MgdG8gdXBkYXRlDQogIG11dGF0ZShlZGl0X2Nvb3Jkc191bmMgPSBgUmVTdXJ2ZXkgcGxvdCAoWS9OKWAgPT0gIlkiICYgUlNfQ09ERSA9PSJJVF8wMDA4IiAmDQogICAgICAgICAgIGlzLm5hKExvbmdpdHVkZSkpICU+JQ0KICAjIEpvaW4gd2l0aCBjb3JyZWN0aW9uMmEgYmFzZWQgb24gUGxvdE9ic2VydmF0aW9uSUQNCiAgIyBSZW5hbWUgY29sdW1ucyB0byB1cGRhdGUgdG8gYXZvaWQgam9pbmluZyBvbiB0aGVzZSBjb2x1bW5zDQogIGxlZnRfam9pbigNCiAgICBjb3JyZWN0aW9uMmEgJT4lDQogICAgICBzZWxlY3QoUGxvdE9ic2VydmF0aW9uSUQsIExvbmdpdHVkZSwgTGF0aXR1ZGUsDQogICAgICAgICAgICAgYExvY2F0aW9uIHVuY2VydGFpbnR5IChtKWApICU+JQ0KICAgICAgcmVuYW1lKExvbmdpdHVkZS5uZXcgPSBMb25naXR1ZGUsIExhdGl0dWRlLm5ldyA9IExhdGl0dWRlLA0KICAgICAgICAgICAgIGBMb2NhdGlvbiB1bmNlcnRhaW50eSAobSkubmV3YCA9IGBMb2NhdGlvbiB1bmNlcnRhaW50eSAobSlgKSAlPiUNCiAgICAgICMgU2V0IGBMb2NhdGlvbiB1bmNlcnRhaW50eSAobSkubmV3YCBhcyBudW1lcmljDQogICAgICBtdXRhdGUoYExvY2F0aW9uIHVuY2VydGFpbnR5IChtKS5uZXdgID0gDQogICAgICAgICAgICAgICBhcy5udW1lcmljKGBMb2NhdGlvbiB1bmNlcnRhaW50eSAobSkubmV3YCkpDQogICAgKSAlPiUNCiAgIyBVcGRhdGUgTG9uZ2l0dWRlLCBMYXRpdHVkZSBhbmQgYExvY2F0aW9uIHVuY2VydGFpbnR5IChtKWB3aXRoIHRoZSBuZXcgdmFsdWVzDQogICMgaWYgZWRpdF9jb29yZHNfdW5jID0gVFJVRQ0KICBtdXRhdGUoDQogICAgTG9uZ2l0dWRlID0gaWZfZWxzZShlZGl0X2Nvb3Jkc191bmMsIExvbmdpdHVkZS5uZXcsIExvbmdpdHVkZSksDQogICAgTGF0aXR1ZGUgPSBpZl9lbHNlKGVkaXRfY29vcmRzX3VuYywgTGF0aXR1ZGUubmV3LCBMYXRpdHVkZSksDQogICAgYExvY2F0aW9uIHVuY2VydGFpbnR5IChtKWAgPSBpZl9lbHNlKA0KICAgICAgZWRpdF9jb29yZHNfdW5jLGBMb2NhdGlvbiB1bmNlcnRhaW50eSAobSkubmV3YCwNCiAgICAgIGBMb2NhdGlvbiB1bmNlcnRhaW50eSAobSlgKQ0KICAgICkgJT4lDQogICMgUmVtb3ZlIHVubmVlZGVkIGNvbHVtbnMNCiAgc2VsZWN0KC1Mb25naXR1ZGUubmV3LCAtTGF0aXR1ZGUubmV3LCAtYExvY2F0aW9uIHVuY2VydGFpbnR5IChtKS5uZXdgKQ0KYGBgDQoNCkNoZWNrIHRoYXQgdGhlcmUgYXJlIG5vIHJvd3Mgd2l0aCBSU19DT0RFID09ICJJVF8wMDA4IiB3aGVyZSBMb25naXR1ZGUsIExhdGl0dWRlIGFuZCBMb2NhdGlvbiB1bmNlcnRhaW50eSAobSkgYXJlIE5BLg0KDQpgYGB7cn0NCm5yb3coDQogIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAgIGZpbHRlcihSU19DT0RFID09ICJJVF8wMDA4IikgJT4lDQogICAgZmlsdGVyKGlzLm5hKExvbmdpdHVkZSkgfCBpcy5uYShMYXRpdHVkZSkgfA0KICAgICAgICAgICAgIGlzLm5hKGBMb2NhdGlvbiB1bmNlcnRhaW50eSAobSlgKSkNCiAgKQ0KYGBgDQoNCiMjIGINCg0KYGBge3J9DQpjb3JyZWN0aW9uMmIgPC0gcmVhZF9leGNlbCgNCiAgaGVyZSgiZGF0YSIsICJyYXciLCAiRGF0YV9jb3JyZWN0aW9uc19JbG9uYSIsICJJc3N1ZTIiLA0KICAgICAgICJERV8wMDMxX2Nvb3JkaW5hdGVzZmlsbGVkLnhsc3giKSkNCmBgYA0KDQpgYGB7cn0NCnByaW50KGNvcnJlY3Rpb24yYiwgd2lkdGggPSBJbmYpDQpgYGANCg0KTmVlZCB0byB1cGRhdGUgTG9uZ2l0dWRlLCBMYXRpdHVkZSBhbmQgTG9jYXRpb24gdW5jZXJ0YWludHkgKG0pIChJIGd1ZXNzIHRoaXMgaXMgY29kZWQgYXMgUFJFQ0lTSU9OKS4NCg0KYGBge3J9DQpucm93KGNvcnJlY3Rpb24yYikNCm5yb3coZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lIGZpbHRlcihSU19DT0RFID09ICJERV8wMDMxIikpDQpucm93KGRiX3Jlc3Vydl91cGRhdGVkICU+JSBmaWx0ZXIoUlNfQ09ERSA9PSAiREVfMDAzMSIgJiBpcy5uYShMb25naXR1ZGUpKSkNCm5yb3coZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogICAgICAgZmlsdGVyKFJTX0NPREUgPT0gIkRFXzAwMzEiICYgaXMubmEoYExvY2F0aW9uIHVuY2VydGFpbnR5IChtKWApKSkNCmBgYA0KDQpBbGwgMzYzIHJvd3Mgd2l0aCBSU19DT0RFID09ICJERV8wMDMxIiBoYXZlIE5BcyBmb3IgY29vcmRpbmF0ZXMgYW5kIExvY2F0aW9uIHVuY2VydGFpbnR5Lg0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkIDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAjIENyZWF0ZSBhbiBVcGRhdGVGbGFnIHRvIG1hcmsgcm93cyB0byB1cGRhdGUNCiAgbXV0YXRlKFVwZGF0ZUZsYWcgPSBgUmVTdXJ2ZXkgcGxvdCAoWS9OKWAgPT0gIlkiICYgUlNfQ09ERSA9PSJERV8wMDMxIikgJT4lDQogICMgSm9pbiB3aXRoIGNvcnJlY3Rpb24yYiBiYXNlZCBvbiBQbG90T2JzZXJ2YXRpb25JRA0KICAjIENvbHVtbnMgdG8gdXBkYXRlIGFscmVhZHkgaGF2ZSBhIGRpZmZlcmVudCBuYW1lLCANCiAgIyBzbyBqb2luIHdpbGwgbm90IGJlIGRvbmUgb24gdGhlc2UgY29sdW1ucw0KICBsZWZ0X2pvaW4oDQogICAgY29ycmVjdGlvbjJiICU+JSBzZWxlY3QoUGxvdE9ic2VydmF0aW9uSUQsIExPTkdJVFVERSwgTEFUSVRVREUsIFBSRUNJU0lPTikNCiAgICApICU+JQ0KICAjIFVwZGF0ZSBMb25naXR1ZGUsIExhdGl0dWRlIGFuZCBgTG9jYXRpb24gdW5jZXJ0YWludHkgKG0pYHdpdGggdGhlIG5ldyB2YWx1ZXMNCiAgIyBpZiBVcGRhdGVGbGFnID0gVFJVRQ0KICBtdXRhdGUoDQogICAgTG9uZ2l0dWRlID0gaWZfZWxzZShVcGRhdGVGbGFnLCBMT05HSVRVREUsIExvbmdpdHVkZSksDQogICAgTGF0aXR1ZGUgPSBpZl9lbHNlKFVwZGF0ZUZsYWcsIExBVElUVURFLCBMYXRpdHVkZSksDQogICAgYExvY2F0aW9uIHVuY2VydGFpbnR5IChtKWAgPSBpZl9lbHNlKA0KICAgICAgVXBkYXRlRmxhZywgUFJFQ0lTSU9OLCBgTG9jYXRpb24gdW5jZXJ0YWludHkgKG0pYCkNCiAgICApICU+JQ0KICAjIFVwZGF0ZSBjb2x1bW4gZWRpdF9jb29yZHNfdW5jIHRvIGxhYmVsIGVkaXRzDQogIG11dGF0ZShlZGl0X2Nvb3Jkc191bmMgPSBpZl9lbHNlKFVwZGF0ZUZsYWcsIFRSVUUsIGVkaXRfY29vcmRzX3VuYykpICU+JQ0KICAjIFJlbW92ZSB1bm5lZWRlZCBjb2x1bW5zDQogIHNlbGVjdCgtVXBkYXRlRmxhZywgLUxPTkdJVFVERSwgLUxBVElUVURFLCAtUFJFQ0lTSU9OKQ0KYGBgDQoNCkNoZWNrIHRoYXQgdGhlcmUgYXJlIG5vIHJvd3Mgd2l0aCBSU19DT0RFID09ICJERV8wMDMxIiB3aGVyZSBMb25naXR1ZGUsIExhdGl0dWRlIGFuZCBMb2NhdGlvbiB1bmNlcnRhaW50eSAobSkgYXJlIE5BLg0KDQpgYGB7cn0NCm5yb3coDQogIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAgIGZpbHRlcihSU19DT0RFID09ICJERV8wMDMxIikgJT4lDQogICAgZmlsdGVyKGlzLm5hKExvbmdpdHVkZSkgfCBpcy5uYShMYXRpdHVkZSkgfA0KICAgICAgICAgICAgIGlzLm5hKGBMb2NhdGlvbiB1bmNlcnRhaW50eSAobSlgKSkNCiAgKQ0KYGBgDQoNCkNoZWNrIGhvdyBtYW55IHJvd3MgaGF2ZSBubyBjb29yZGluYXRlcyBhZnRlciB0aGVzZSB1cGRhdGVzLg0KDQpgYGB7cn0NCm5yb3coZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogICAgICAgZmlsdGVyKGBSZVN1cnZleSBwbG90IChZL04pYCA9PSAiWSIgJiBpcy5uYShMb25naXR1ZGUpKSkNCmBgYA0KDQojIENvcnJlY3Rpb24gSVNTVUUgMw0KDQpUZXh0IGluIElsb25hJ3MgZW1haWxzOiANCg0KRW1haWwgMToNCg0KSXNzdWUzIDogYWdhaW4gSSBoYXZlIHByZXBhcmVkIHJlbWFya3MgZm9yIGRhdGFzZXRzIChmaWxlIDIwMF9Jc3N1ZTNfZGF0YXNldHMueGxzeCkg4oCTIGluIHNvbWUgY2FzZXMgZGlmZmVyZW50IGNvb3JkaW5hdGVzIGZvciBvbmUgcGxvdCBjb3JyZWN0LiBJbiBzb21lIGNhc2VzIGNsZWFyIGVycm9yIChBZ2FpbiBlLW1haWwgdG8gY3VzdG9kaWFucyBvZiB0aGVzZSBkYXRhc2V0cykgLiBBbmQgYWxzbyBJIGFtIHNlbmRpbmcgeW91IGZpbGVzIHdpdGggY29ycmVjdGVkIGNvb3JkaW5hdGVzLiBFc3BlY2lhbGx5IGZvciBpc3N1ZSAzIHZlcnkgaW1wb3J0YW50IGlzIGRlc2lnbiBvZiByZXN1cnZleSBkYXRhc2V0IOKAkyBzbyBwbGVhc2UgcmVhZCByZW1hcmsgaW4g4oCcMjAwX0lzc3VlM19kYXRhc2V0cy54bHN44oCdLg0KDQpBdHRhY2hlZDogIjIwMF9Jc3N1ZTNfZGF0YXNldHMueGxzeCIsICJDWl8wMDE5XzA0OF9jb3JyZWN0ZWQueGxzeCIsICJDWl8wMDE5X2NvcnJlY3RlZC54bHN4IiwgIklzc3VlM19DWl8wMDAxX2NvcnJlY3RlZC50eHQiLCAiSXNzdWUzX0VTXzAwMDNfY29ycmVjdGVkLnhsc3giLiANCg0KSW4gIjIwMF9Jc3N1ZTNfZGF0YXNldHMueGxzeCIgdGhlcmUgaXMgYSBsaXN0IG9mIGRhdGFzZXRzIHdpdGggdGhpcyBpc3N1ZS4NCg0KRW1haWwgMjoNCg0KSSBhbSBzZW5kaW5nIHlvdSBjb3JyZWN0ZWQgY29vcmRpbmF0ZSBmb3IgUExfMDAwOSDigJMgb25lIGNvb3JkaW5hdGUgYW5kIGNvZGluZyBvZiAxIHBsb3Qg4oCTIGNoYW5nZXMgaW4gcmVkLg0KDQpBdHRhY2hlZDogIklzc3VlM19QTF8wMDA5X2Nvb3JlY3RlZC54bHN4Ii4NCg0KSSB3aWxsIG9ubHkgdXBkYXRlIGVkaXRzX0FWIGluIHRoZSBjYXNlcyB3aGVyZSBjb29yZGluYXRlcyBhcmUgYWRkZWQuDQoNCkJhc2VkIG9uIHRoZSBpbmZvIGluIHRoZSAicmVtYXJrIiBjb2x1bW4gaW4gMjAwX0lzc3VlM19kYXRhc2V0cy54bHN4IiwgSSBjcmVhdGVkIGEgbmV3IGNvbHVtbiAiY29ycmVjdCIgKGluIGEgbmV3IGZpbGUsIGxvY2F0ZWQgaW4gdGhlIGRhdGEvZWRpdGVkIGZvbGRlcikuIFRoaXMgY29sdW1uIHNob3dzOg0KDQotIG9rOiBpZiBpdCBpcyBPSyB0byBoYXZlIGRpZmZlcmVudCBjb29yZGluYXRlcyBmb3IgZGlmZmVyZW50IG9ic2VydmF0aW9ucyBvZiB0aGUgc2FtZSBwbG90IChyZXNhbXBsaW5nLCBldGMpLg0KDQotIG5vdF9vazogaWYgaXMgbm90IE9LIHRvIGhhdmUgZGlmZmVyZW50IGNvb3JkaW5hdGVzIGZvciBkaWZmZXJlbnQgb2JzZXJ2YXRpb25zIG9mIHRoZSBzYW1lIHBsb3QgKGluIG1vc3Qgb2YgdGhlc2UgY2FzZXMgSWxvbmEgaGFzIHNlbnQgYW4gZW1haWwgdG8gY3VzdG9kaWFuIG9mIHRoZSBkYXRhc2V0KS4NCg0KLSB0b19jb3JyZWN0OiB3aGVuIGNvcnJlY3Rpb25zIGhhdmUgdG8gYmUgbWFkZSBiYXNlZCBvbiBpbmZvIG9uIHRoZSBmaWxlcyB0aGF0IElsb25hIGhhcyBzZW50Lg0KDQpJIHdpbGwgdXNlIHRoaXMgaW5mbyB0byB1cGRhdGUgdGhlIGVkaXRzX0FWIGNvbHVtbi4NCg0KUmVhZCB0aGUgZmlsZSBpbiB0aGUgZWRpdGVkIGRhdGEgZm9sZGVyLg0KDQpgYGB7cn0NCmlzc3VlM19kYXRhc2V0cyA8LSByZWFkX2V4Y2VsKA0KICBoZXJlKCJkYXRhIiwgImVkaXRlZCIsIjIwMF9Jc3N1ZTNfZGF0YXNldHNfZWRpdGVkQVYueGxzeCIpKQ0KYGBgDQoNCkNyZWF0ZSB0d28gbmV3IGNvbHVtbnMgaW4gZGJfcmVzdXJ2X3VwZGF0ZWQ6IGNvb3JkaW5hdGVzX2VxdWFsIGluZGljYXRpbmcgaWYgY29vcmRpbmF0ZXMgYXJlIGV4YWN0bHkgZXF1YWwgYmV0d2VlbiBSZVN1cnZleSBvYnNlcnZhdGlvbnMsIGFuZCBjb29yZGluYXRlc19jb25zaXN0ZW50LCBpbmRpY2F0aW5nIGlmIGNvb3JkaW5hdGVzIGFyZSBjb25zaXN0ZW50IGJldHdlZW4gUmVTdXJ2ZXkgb2JzZXJ2YXRpb25zIChjb25zaXN0ZW50IG1lYW5pbmcgdGhhdCBkaWZmZXJlbmNlIDwgMC4wMDEgZGVncmVlcykuDQoNCmBgYHtyfQ0KIyBEZWZpbmUgYSB0aHJlc2hvbGQgKGUuZy4sIDAuMDAxIGRlZ3JlZXMgZm9yIGxvbmdpdHVkZS9sYXRpdHVkZSBkaWZmZXJlbmNlcykNCnRocmVzaG9sZCA8LSAwLjAwMQ0KDQpkYl9yZXN1cnZfdXBkYXRlZCA8LSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgZ3JvdXBfYnkoUlNfQ09ERSwgYFJlU3VydmV5IHNpdGVgLCBgUmVTdXJ2ZXkgcGxvdGApICU+JQ0KICBtdXRhdGUoDQogICAgbG9uX3JhbmdlID0gaWZlbHNlKGFsbChpcy5uYShMb25fdXBkYXRlZCkpLCBOQSwNCiAgICAgICAgICAgICAgICAgICAgICAgIG1heChMb25fdXBkYXRlZCwgbmEucm0gPSBUKSAtIA0KICAgICAgICAgICAgICAgICAgICAgICAgIG1pbihMb25fdXBkYXRlZCwgbmEucm0gPSBUKSksDQogICAgbGF0X3JhbmdlID0gaWZlbHNlKGFsbChpcy5uYShMYXRfdXBkYXRlZCkpLCBOQSwNCiAgICAgICAgICAgICAgICAgICAgICAgIG1heChMYXRfdXBkYXRlZCwgbmEucm0gPSBUKSAtIA0KICAgICAgICAgICAgICAgICAgICAgICAgIG1pbihMYXRfdXBkYXRlZCwgbmEucm0gPSBUKSksDQogICAgY29vcmRpbmF0ZXNfZXF1YWwgPSBpZmVsc2UoaXMubmEoTG9uX3VwZGF0ZWQpICYgaXMubmEoTGF0X3VwZGF0ZWQpLCBOQSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb25fcmFuZ2UgPT0gMCAmIGxhdF9yYW5nZSA9PSAwKSwNCiAgICBjb29yZGluYXRlc19jb25zaXN0ZW50ID0gaWZlbHNlKGlzLm5hKExvbl91cGRhdGVkKSAmIGlzLm5hKExhdF91cGRhdGVkKSwgTkEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb25fcmFuZ2UgPCB0aHJlc2hvbGQgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF0X3JhbmdlIDwgdGhyZXNob2xkKQ0KICApICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIHNlbGVjdCgtbG9uX3JhbmdlLCAtbGF0X3JhbmdlKQ0KYGBgDQoNClNlZSBpZiBjb3VudCBvZiByb3dzIGluIGRiX3Jlc3Vydl91cGRhdGVkIHdoZXJlIGRpZmZlcmVudCBSZVN1cnZleSBvYnNlcnZhdGlvbnMgd2l0aGluIHRoZSBzYW1lIHBsb3QgaGF2ZSBkaWZmZXJlbnQgY29vcmRpbmF0ZXMgbWF0Y2hlcyBjb3VudCBpbiBJbG9uYSdzIGZpbGUsIGFuZCBrZWVwIGNhc2VzIHdoZXJlIHJvdyBjb3VudCBpcyBkaWZmZXJlbnQuDQoNCmBgYHtyfQ0KaXNzdWUzX2RhdGFzZXRzX2RpZmZfY291bnRzIDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICBmaWx0ZXIoY29vcmRpbmF0ZXNfZXF1YWwgPT0gRkFMU0UpICU+JQ0KICBncm91cF9ieShSU19DT0RFLCBEYXRhc2V0KSAlPiUNCiAgc3VtbWFyaXplKGNvdW50ID0gbigpKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICAjIEFkZCBpbmZvIG9uIElsb25hJ3MgZmlsZQ0KICBmdWxsX2pvaW4oDQogICAgaXNzdWUzX2RhdGFzZXRzICU+JSBzZWxlY3QoUlNfQ09ERSwgRGF0YXNldCwgQ291bnRPZkRhdGFzZXQpICU+JQ0KICAgICAgbXV0YXRlKENvdW50T2ZEYXRhc2V0ID0gYXMuaW50ZWdlcihDb3VudE9mRGF0YXNldCkpDQogICkgJT4lDQogICMgS2VlcCByZWNvcmRzIHdoZXJlIGNvdW50cyBhcmUgZGlmZmVyZW50IGJldHdlZW4gZGJfcmVzdXJ2X3VwZGF0ZWQNCiAgIyBhbmQgSWxvbmEncyBmaWxlDQogIGZpbHRlcihjb3VudCAhPSBDb3VudE9mRGF0YXNldCkNCmBgYA0KDQpgYGB7cn0NCmlzc3VlM19kYXRhc2V0c19kaWZmX2NvdW50cw0KYGBgDQoNClRoZXNlIGNvdW50cyBtaWdodCBiZSBkaWZmZXJlbnQgYmVjYXVzZSBvZiBvdGhlciBjaGFuZ2VzLg0KDQojIyBEaWZmZXJlbnQgY29vcmRpbmF0ZXMgb2sgLyBub3Qgb2sNCg0KQWNjb3JkaW5nIHRvIElsb25hJ3MgcmVtYXJrczogY2FzZXMgd2hlcmUgaXQgaXMgT0sgLyBub3QgT0sgdG8gaGF2ZSBkaWZmZXJlbnQgY29vcmRpbmF0ZXMgZm9yIGRpZmZlcmVudCBvYnNlcnZhdGlvbnMgb2YgdGhlIHNhbWUgcGxvdCAoT0sgd2hlbiByZXNhbXBsaW5nLCBldGMpLg0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkIDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAjIEpvaW4gaXNzdWUzX2RhdGFzZXRzDQogIGxlZnRfam9pbihpc3N1ZTNfZGF0YXNldHMgJT4lIHNlbGVjdCgtQ291bnRPZkRhdGFzZXQsIC1yZW1hcmspKSAlPiUNCiAgIyBDcmVhdGUgYSBjb2x1bW4gZWRpdF9kaWZmX2Nvb3Jkc19vayB0byBtYXJrIHJvd3MgdG8gdXBkYXRlDQogIG11dGF0ZShlZGl0X2RpZmZfY29vcmRzX29rID0NCiAgICAgICAgICAgaWZlbHNlKA0KICAgICAgICAgICAgIChjb29yZGluYXRlc19lcXVhbCA9PSBGQUxTRSB8IGlzLm5hKGNvb3JkaW5hdGVzX2VxdWFsKSkgJg0KICAgICAgICAgICAgICAgY29ycmVjdCA9PSAib2siLCBUUlVFLCBGQUxTRSkpICU+JQ0KICAjIENyZWF0ZSBhIGNvbHVtbiBlZGl0X2RpZmZfY29vcmRzX25vdF9vayB0byBtYXJrIHJvd3MgdG8gdXBkYXRlDQogIG11dGF0ZShlZGl0X2RpZmZfY29vcmRzX25vdF9vayA9DQogICAgICAgICAgIGlmZWxzZSgNCiAgICAgICAgICAgICAoY29vcmRpbmF0ZXNfZXF1YWwgPT0gRkFMU0UgfCBpcy5uYShjb29yZGluYXRlc19lcXVhbCkpICYNCiAgICAgICAgICAgICAgIGNvcnJlY3QgPT0gIm5vdF9vayIsIFRSVUUsIEZBTFNFKSkgJT4lDQogICMgU2V0IE5BIHZhbHVlcyBmb3IgZWRpdF9kaWZmX2Nvb3Jkc19vayBhbmQgZWRpdF9kaWZmX2Nvb3Jkc19ub3Rfb2sgYXMgRkFMU0UNCiAgbXV0YXRlKGVkaXRfZGlmZl9jb29yZHNfb2sgPSBpZmVsc2UoaXMubmEoZWRpdF9kaWZmX2Nvb3Jkc19vayksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZBTFNFLCBlZGl0X2RpZmZfY29vcmRzX29rKSwNCiAgICAgICAgIGVkaXRfZGlmZl9jb29yZHNfbm90X29rID0gaWZlbHNlKGlzLm5hKGVkaXRfZGlmZl9jb29yZHNfbm90X29rKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRkFMU0UsIGVkaXRfZGlmZl9jb29yZHNfbm90X29rKSkNCmBgYA0KDQpXZSBoYXZlIHRvIGNvcnJlY3QgdGhvc2UgcmVjb3JkcyB3aGVyZSBjb3JyZWN0ID09ICJ0b19jb3JyZWN0IiBiYXNlZCBvbiBJbG9uYSdzIGZpbGVzIG9yIG9uIGluZm8gb24gIjIwMF9Jc3N1ZTNfZGF0YXNldHMueGxzeCIuDQoNCiMjIENvcnJlY3QgY29vcmRpbmF0ZXMNCg0KV2hpY2ggb25lcyB0byBjb3JyZWN0Pw0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkICU+JSBmaWx0ZXIoY29ycmVjdCA9PSAidG9fY29ycmVjdCIpICU+JQ0KICBkaXN0aW5jdChSU19DT0RFLCBEYXRhc2V0LCBgUmVTdXJ2ZXkgcHJvamVjdGApICU+JSBhcnJhbmdlKFJTX0NPREUpDQpgYGANCg0KIyMjIENaXzAwMDENCg0KUmVhZCB0aGUgZmlsZSBpbiB0aGUgcmF3IGRhdGEgZm9sZGVyLg0KDQpgYGB7cn0NCmlzc3VlM19DWl8wMDAxIDwtIHJlYWRfdHN2KA0KICBoZXJlKCJkYXRhIiwgInJhdyIsICJEYXRhX2NvcnJlY3Rpb25zX0lsb25hIiwgIklzc3VlMyIsDQogICAgICAgIklzc3VlM19DWl8wMDAxX2NvcnJlY3RlZC50eHQiKSkNCmBgYA0KTmVlZCB0byB1cGRhdGUgTG9uZ2l0dWRlLCBMYXRpdHVkZSBhbmQgTG9jYXRpb24gdW5jZXJ0YWludHkgKG0pIChJIGd1ZXNzIHRoaXMgaXMgY29kZWQgYXMgUFJFQ0lTSU9OKS4NCg0KSWxvbmE6IFBsZWFzZSBtaW5kIHRoYXQgQ1ogZmlsZXMgKGFsbCByZXN1cnZleSBkYXRhc2V0cyB3aXRoIHJzX2NvZGVzIHRoYXQgYmVnaW5zIHdpdGgg4oCcQ1rigJ0gaGF2ZSBzcGVjaWFsIGZvcm1hdCBvZiBjb29yZGluYXRlcyBub3QgZGVjaW1hbCBkZWdyZWVzIGJ1dCBERE1NU1MuU1MpLg0KDQpXZSBuZWVkIHRvIGNvbnZlcnQgY29vcmRpbmF0ZXMgdG8gZGVjaW1hbCBkZWdyZWVzLg0KDQpGdW5jdGlvbiB0byBjb252ZXJ0IERETU1TUy5TUyBmb3JtYXQgdG8gZGVjaW1hbCBkZWdyZWVzOg0KDQpgYGB7cn0NCmNvbnZlcnRfdG9fZGVjaW1hbCA8LSBmdW5jdGlvbihkZG1tc3MpIHsNCiAgIyBDb252ZXJ0IHRvIGNoYXJhY3RlciB0byBoYW5kbGUgdGhlIGRhdGEgYXMgc3RyaW5ncw0KICBkZG1tc3MgPC0gYXMuY2hhcmFjdGVyKGRkbW1zcykNCiAgDQogICMgRXh0cmFjdCB0aGUgZGVncmVlLCBtaW51dGUsIGFuZCBzZWNvbmQgcGFydHMNCiAgaWYgKG5jaGFyKGRkbW1zcykgPj0gNikgeyAgIyBFbnN1cmUgaXQncyBhdCBsZWFzdCA2IGNoYXJhY3RlcnMgbG9uZyAoRERNTVNTKQ0KICAgIGRlZ3JlZXMgPC0gYXMubnVtZXJpYyhzdWJzdHIoZGRtbXNzLCAxLCAyKSkgICMgRmlyc3QgMiBkaWdpdHMgZm9yIGRlZ3JlZXMNCiAgICBtaW51dGVzIDwtIGFzLm51bWVyaWMoc3Vic3RyKGRkbW1zcywgMywgNCkpICAjIE5leHQgMiBkaWdpdHMgZm9yIG1pbnV0ZXMNCiAgICAjIFJlc3QgZm9yIHNlY29uZHMgKGluY2x1ZGluZyBkZWNpbWFscyBpZiBhbnkpDQogICAgc2Vjb25kcyA8LSBhcy5udW1lcmljKHN1YnN0cihkZG1tc3MsIDUsIG5jaGFyKGRkbW1zcykpKQ0KICB9IGVsc2Ugew0KICAgIHJldHVybihOQSkgICMgUmV0dXJuIE5BIGlmIHRoZSBmb3JtYXQgZG9lc24ndCBtYXRjaCBleHBlY3RlZA0KICB9DQogIA0KICAjIENvbnZlcnQgdG8gZGVjaW1hbCBkZWdyZWVzDQogIGRlY2ltYWxfZGVncmVlcyA8LSBkZWdyZWVzICsgKG1pbnV0ZXMgLyA2MCkgKyAoc2Vjb25kcyAvIDM2MDApDQogIA0KICByZXR1cm4oZGVjaW1hbF9kZWdyZWVzKQ0KfQ0KYGBgDQoNCkFwcGx5IHRoZSBjb252ZXJzaW9uIGZ1bmN0aW9uIHRvIHRoZSBsb25naXR1ZGUgYW5kIGxhdGl0dWRlIGNvbHVtbnM6DQoNCmBgYHtyfQ0KaXNzdWUzX0NaXzAwMDEgPC0gaXNzdWUzX0NaXzAwMDEgJT4lDQogIG11dGF0ZShMT05HSVRVREVfZGVjaW1hbCA9IHNhcHBseShMT05HSVRVREUsIGNvbnZlcnRfdG9fZGVjaW1hbCksDQogICAgICAgICBMQVRJVFVERV9kZWNpbWFsID0gc2FwcGx5KExBVElUVURFLCBjb252ZXJ0X3RvX2RlY2ltYWwpKQ0KYGBgDQoNCk51bWJlciBvZiByb3dzIGluIGlzc3VlM19DWl8wMDAxID0gbnVtYmVyIG9mIHJvd3MgaW4gZGIgd2l0aCB0aGF0IFJTX0NPREU6DQoNCmBgYHtyfQ0KbnJvdyhpc3N1ZTNfQ1pfMDAwMSkNCm5yb3coZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lIGZpbHRlcihSU19DT0RFID09ICJDWl8wMDAxIikpDQpgYGANCg0KVXBkYXRlIGRiX3Jlc3Vydl91cGRhdGVkIG9ubHkgZm9yIGNhc2VzIHdoZXJlIFJTX0NPREUgPT0gIkNaXzAwMDEiLg0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkIDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAjIENyZWF0ZSBhbiBVcGRhdGVGbGFnIHRvIG1hcmsgcm93cyB0byB1cGRhdGUNCiAgbXV0YXRlKFVwZGF0ZUZsYWcgPSBSU19DT0RFID09ICJDWl8wMDAxIikgJT4lDQogICMgSm9pbiB3aXRoIGlzc3VlM19DWl8wMDAxIGJhc2VkIG9uIFBsb3RPYnNlcnZhdGlvbklEDQogIGxlZnRfam9pbihpc3N1ZTNfQ1pfMDAwMSAlPiUNCiAgICAgICAgICAgICAgc2VsZWN0KFBsb3RPYnNlcnZhdGlvbklELCBMT05HSVRVREVfZGVjaW1hbCwgTEFUSVRVREVfZGVjaW1hbCwNCiAgICAgICAgICAgICAgICAgICAgIFBSRUNJU0lPTikpICU+JQ0KICAjIFVwZGF0ZSBMb25naXR1ZGUsIExhdGl0dWRlIGFuZCBgTG9jYXRpb24gdW5jZXJ0YWludHkgKG0pYHdpdGggdGhlIG5ldyB2YWx1ZXMNCiAgIyBpZiBVcGRhdGVGbGFnID0gVFJVRQ0KICBtdXRhdGUoDQogICAgTG9uZ2l0dWRlID0gaWZfZWxzZShVcGRhdGVGbGFnLCBMT05HSVRVREVfZGVjaW1hbCwgTG9uZ2l0dWRlKSwNCiAgICBMYXRpdHVkZSA9IGlmX2Vsc2UoVXBkYXRlRmxhZywgTEFUSVRVREVfZGVjaW1hbCwgTGF0aXR1ZGUpLA0KICAgIGBMb2NhdGlvbiB1bmNlcnRhaW50eSAobSlgID0gaWZfZWxzZSgNCiAgICAgIFVwZGF0ZUZsYWcsIFBSRUNJU0lPTiwgYExvY2F0aW9uIHVuY2VydGFpbnR5IChtKWApDQogICAgKSAlPiUNCiAgIyBVcGRhdGUgY29sdW1uIGVkaXRfY29vcmRzX3VuYyB0byBsYWJlbCBlZGl0cw0KICBtdXRhdGUoZWRpdF9jb29yZHNfdW5jID0gaWZfZWxzZShVcGRhdGVGbGFnLCBUUlVFLCBlZGl0X2Nvb3Jkc191bmMpKSAlPiUNCiAgIyBSZW1vdmUgdW5uZWVkZWQgY29sdW1ucw0KICBzZWxlY3QoLVVwZGF0ZUZsYWcsIC1MT05HSVRVREVfZGVjaW1hbCwgLUxBVElUVURFX2RlY2ltYWwsIC1QUkVDSVNJT04pDQpgYGANCg0KIyMjIENaXzAwMDUNCg0KYGBge3J9DQpucm93KGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAgICAgIGZpbHRlcihSU19DT0RFID09ICJDWl8wMDA1IiAmIGNvb3JkaW5hdGVzX2VxdWFsID09IEZBTFNFKSkNCmBgYA0KDQpJbG9uYSdzIHJlbWFyazogdGhlIGxhc3Qgb2JzZXJ2YXRpb24gb2YgdGhpcyBwbG90IHdhcyBjb3JyZWN0ZWQgc2FtZSBjb29yZGluYXRlcyBhcyBhbGwgb3RoZXIgb2JzZXJ2YXRpb25zLg0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICBmaWx0ZXIoUlNfQ09ERSA9PSAiQ1pfMDAwNSIgJiBjb29yZGluYXRlc19lcXVhbCA9PSBGQUxTRSkgJT4lDQogIHNlbGVjdChSU19DT0RFLCBgUmVTdXJ2ZXkgcGxvdGAsIGBSZVN1cnZleSBvYnNlcnZhdGlvbmAsIExvbmdpdHVkZSwgTGF0aXR1ZGUpDQpgYGANCg0KR2V0IHZhbHVlcyBvZiBsb25naXR1ZGUgYW5kIGxhdGl0dWRlIGZyb20gYWxsIG90aGVyIG9ic2VydmF0aW9ucyB0byBjb3JyZWN0IHRoZSBsYXN0IG9ic2VydmF0aW9uIG9mIHRoaXMgcGxvdDoNCg0KYGBge3J9DQpsb25naXR1ZGUgPC0gYXMubnVtZXJpYygNCiAgZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogICAgZmlsdGVyKFJTX0NPREUgPT0gIkNaXzAwMDUiICYgY29vcmRpbmF0ZXNfZXF1YWwgPT0gRkFMU0UpICU+JQ0KICAgIHNlbGVjdChSU19DT0RFLCBgUmVTdXJ2ZXkgcGxvdGAsIGBSZVN1cnZleSBvYnNlcnZhdGlvbmAsIExvbmdpdHVkZSwgTGF0aXR1ZGUpICU+JQ0KICAgIG11dGF0ZShgUmVTdXJ2ZXkgb2JzZXJ2YXRpb25gID0gYXMubnVtZXJpYyhgUmVTdXJ2ZXkgb2JzZXJ2YXRpb25gKSkgJT4lDQogICAgZmlsdGVyKGBSZVN1cnZleSBvYnNlcnZhdGlvbmAgPT0gbWluKGBSZVN1cnZleSBvYnNlcnZhdGlvbmApKSAlPiUNCiAgICBzZWxlY3QoTG9uZ2l0dWRlKQ0KICApDQoNCmxhdGl0dWRlIDwtIGFzLm51bWVyaWMoDQogIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAgIGZpbHRlcihSU19DT0RFID09ICJDWl8wMDA1IiAmIGNvb3JkaW5hdGVzX2VxdWFsID09IEZBTFNFKSAlPiUNCiAgICBzZWxlY3QoUlNfQ09ERSwgYFJlU3VydmV5IHBsb3RgLCBgUmVTdXJ2ZXkgb2JzZXJ2YXRpb25gLCBMb25naXR1ZGUsIExhdGl0dWRlKSAlPiUNCiAgICBtdXRhdGUoYFJlU3VydmV5IG9ic2VydmF0aW9uYCA9IGFzLm51bWVyaWMoYFJlU3VydmV5IG9ic2VydmF0aW9uYCkpICU+JQ0KICAgIGZpbHRlcihgUmVTdXJ2ZXkgb2JzZXJ2YXRpb25gID09IG1pbihgUmVTdXJ2ZXkgb2JzZXJ2YXRpb25gKSkgJT4lDQogICAgc2VsZWN0KExhdGl0dWRlKQ0KKQ0KYGBgDQoNCkNvcnJlY3QgdGhlIGxhc3Qgb2JzZXJ2YXRpb24gb2YgdGhpcyBwbG90Og0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkIDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAjIENyZWF0ZSBhbiBVcGRhdGVGbGFnIHRvIG1hcmsgdGhlIHJvdyB0byB1cGRhdGUNCiAgbXV0YXRlKFVwZGF0ZUZsYWcgPSBSU19DT0RFID09ICJDWl8wMDA1IiAmIGNvb3JkaW5hdGVzX2VxdWFsID09IEZBTFNFICYNCiAgICAgICAgICAgYFJlU3VydmV5IG9ic2VydmF0aW9uYCA9PSAyMDIxKSAlPiUNCiAgIyBVcGRhdGUgTG9uZ2l0dWRlIGFuZCBMYXRpdHVkZSB3aXRoIHRoZSB2YWx1ZXMgYWJvdmUgaWYgVXBkYXRlRmxhZyA9IFRSVUUNCiAgbXV0YXRlKA0KICAgIExvbmdpdHVkZSA9IGlmX2Vsc2UoVXBkYXRlRmxhZywgbG9uZ2l0dWRlLCBMb25naXR1ZGUpLA0KICAgIExhdGl0dWRlID0gaWZfZWxzZShVcGRhdGVGbGFnLCBsYXRpdHVkZSwgTGF0aXR1ZGUpDQogICAgKSAlPiUNCiAgIyBDcmVhdGUgYSBjb2x1bW4gZWRpdF9jb29yZHMgdG8gbWFyayB0aGUgcm93IHRvIHVwZGF0ZQ0KICBtdXRhdGUoZWRpdF9jb29yZHMgPSBSU19DT0RFID09ICJDWl8wMDA1IiAmIGNvb3JkaW5hdGVzX2VxdWFsID09IEZBTFNFICYNCiAgICAgICAgICAgYFJlU3VydmV5IG9ic2VydmF0aW9uYCA9PSAyMDIxKSAlPiUNCiAgIyBSZW1vdmUgdW5uZWVkZWQgY29sdW1uDQogIHNlbGVjdCgtVXBkYXRlRmxhZykNCmBgYA0KDQojIyMgQ1pfMDAxOQ0KDQpSZWFkIHRoZSBmaWxlIGluIHRoZSByYXcgZGF0YSBmb2xkZXIuDQoNCmBgYHtyfQ0KaXNzdWUzX0NaXzAwMTkgPC0gcmVhZF9leGNlbCgNCiAgaGVyZSgiZGF0YSIsICJyYXciLCAiRGF0YV9jb3JyZWN0aW9uc19JbG9uYSIsICJJc3N1ZTMiLA0KICAgICAgICJDWl8wMDE5X2NvcnJlY3RlZC54bHN4IikpDQpgYGANCg0KTmVlZCB0byB1cGRhdGUgTG9uZ2l0dWRlLCBMYXRpdHVkZSBhbmQgTG9jYXRpb24gdW5jZXJ0YWludHkgKG0pIChJIGd1ZXNzIHRoaXMgaXMgY29kZWQgYXMgUFJFQ0lTSU9OKS4NCg0KSWxvbmE6IFBsZWFzZSBtaW5kIHRoYXQgQ1ogZmlsZXMgKGFsbCByZXN1cnZleSBkYXRhc2V0cyB3aXRoIHJzX2NvZGVzIHRoYXQgYmVnaW5zIHdpdGgg4oCcQ1rigJ0gaGF2ZSBzcGVjaWFsIGZvcm1hdCBvZiBjb29yZGluYXRlcyBub3QgZGVjaW1hbCBkZWdyZWVzIGJ1dCBERE1NU1MuU1MpLg0KDQpXZSBuZWVkIHRvIGNvbnZlcnQgY29vcmRpbmF0ZXMgdG8gZGVjaW1hbCBkZWdyZWVzLg0KDQpBcHBseSB0aGUgY29udmVyc2lvbiBmdW5jdGlvbiB0byB0aGUgbG9uZ2l0dWRlIGFuZCBsYXRpdHVkZSBjb2x1bW5zOg0KDQpgYGB7cn0NCmlzc3VlM19DWl8wMDE5IDwtIGlzc3VlM19DWl8wMDE5ICU+JQ0KICBtdXRhdGUoTE9OR0lUVURFX2RlY2ltYWwgPSBzYXBwbHkoTE9OR0lUVURFLCBjb252ZXJ0X3RvX2RlY2ltYWwpLA0KICAgICAgICAgTEFUSVRVREVfZGVjaW1hbCA9IHNhcHBseShMQVRJVFVERSwgY29udmVydF90b19kZWNpbWFsKSkNCmBgYA0KDQojIyMjIENaXzAwMTlfMDAyDQoNCmBgYHtyfQ0KbnJvdyhpc3N1ZTNfQ1pfMDAxOSAlPiUgZmlsdGVyKFJTX0NPREUgPT0gIkNaXzAwMTlfMDAyIikpDQpucm93KGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAgICAgIGZpbHRlcihSU19DT0RFID09ICJDWl8wMDE5XzAwMiIgJiBjb29yZGluYXRlc19lcXVhbCA9PSBGQUxTRSkpDQpgYGANCg0KVXBkYXRlIGRiX3Jlc3Vydl91cGRhdGVkLg0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkIDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAjIENyZWF0ZSBhbiBVcGRhdGVGbGFnIHRvIG1hcmsgcm93cyB0byB1cGRhdGUNCiAgbXV0YXRlKFVwZGF0ZUZsYWcgPSBSU19DT0RFID09ICJDWl8wMDE5XzAwMiIgJiBjb29yZGluYXRlc19lcXVhbCA9PSBGQUxTRSkgJT4lDQogICMgSm9pbiB3aXRoIGlzc3VlM19DWl8wMDE5IGJhc2VkIG9uIFJFTEVWRV9OUg0KICBsZWZ0X2pvaW4oaXNzdWUzX0NaXzAwMTkgJT4lDQogICAgICAgICAgICAgIHNlbGVjdChSRUxFVkVfTlIsIExPTkdJVFVERV9kZWNpbWFsLCBMQVRJVFVERV9kZWNpbWFsLA0KICAgICAgICAgICAgICAgICAgICAgUFJFQ0lTSU9OKSwNCiAgICAgICAgICAgIGpvaW5fYnkoYFRWMiByZWxldsOpIG51bWJlcmAgPT0gUkVMRVZFX05SKSkgJT4lDQogICMgVXBkYXRlIExvbmdpdHVkZSwgTGF0aXR1ZGUgYW5kIGBMb2NhdGlvbiB1bmNlcnRhaW50eSAobSlgd2l0aCB0aGUgbmV3IHZhbHVlcw0KICAjIGlmIFVwZGF0ZUZsYWcgPSBUUlVFDQogIG11dGF0ZSgNCiAgICBMb25naXR1ZGUgPSBpZl9lbHNlKFVwZGF0ZUZsYWcsIExPTkdJVFVERV9kZWNpbWFsLCBMb25naXR1ZGUpLA0KICAgIExhdGl0dWRlID0gaWZfZWxzZShVcGRhdGVGbGFnLCBMQVRJVFVERV9kZWNpbWFsLCBMYXRpdHVkZSksDQogICAgYExvY2F0aW9uIHVuY2VydGFpbnR5IChtKWAgPSBpZl9lbHNlKA0KICAgICAgVXBkYXRlRmxhZywgUFJFQ0lTSU9OLCBgTG9jYXRpb24gdW5jZXJ0YWludHkgKG0pYCkNCiAgICApICU+JQ0KICAjIFVwZGF0ZSBjb2x1bW4gZWRpdF9jb29yZHNfdW5jIHRvIGxhYmVsIGVkaXRzDQogIG11dGF0ZShlZGl0X2Nvb3Jkc191bmMgPSBpZl9lbHNlKFVwZGF0ZUZsYWcsIFRSVUUsIGVkaXRfY29vcmRzX3VuYykpICU+JQ0KICAjIFJlbW92ZSB1bm5lZWRlZCBjb2x1bW5zDQogIHNlbGVjdCgtVXBkYXRlRmxhZywgLUxPTkdJVFVERV9kZWNpbWFsLCAtTEFUSVRVREVfZGVjaW1hbCwgLVBSRUNJU0lPTikNCmBgYA0KDQojIyMjIENaXzAwMTlfMDEzDQoNCmBgYHtyfQ0KbnJvdyhpc3N1ZTNfQ1pfMDAxOSAlPiUgZmlsdGVyKFJTX0NPREUgPT0gIkNaXzAwMTlfMDEzIikpDQpucm93KGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAgICAgIGZpbHRlcihSU19DT0RFID09ICJDWl8wMDE5XzAxMyIpKQ0KYGBgDQoNClVwZGF0ZSBkYl9yZXN1cnZfdXBkYXRlZC4NCg0KYGBge3J9DQpkYl9yZXN1cnZfdXBkYXRlZCA8LSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgIyBDcmVhdGUgYW4gVXBkYXRlRmxhZyB0byBtYXJrIHJvd3MgdG8gdXBkYXRlDQogIG11dGF0ZShVcGRhdGVGbGFnID0gUlNfQ09ERSA9PSAiQ1pfMDAxOV8wMTMiKSAlPiUNCiAgIyBKb2luIHdpdGggaXNzdWUzX0NaXzAwMTkgYmFzZWQgb24gUkVMRVZFX05SDQogIGxlZnRfam9pbihpc3N1ZTNfQ1pfMDAxOSAlPiUNCiAgICAgICAgICAgICAgc2VsZWN0KFJFTEVWRV9OUiwgTE9OR0lUVURFX2RlY2ltYWwsIExBVElUVURFX2RlY2ltYWwsDQogICAgICAgICAgICAgICAgICAgICBQUkVDSVNJT04pLA0KICAgICAgICAgICAgam9pbl9ieShgVFYyIHJlbGV2w6kgbnVtYmVyYCA9PSBSRUxFVkVfTlIpKSAlPiUNCiAgIyBVcGRhdGUgTG9uZ2l0dWRlLCBMYXRpdHVkZSBhbmQgYExvY2F0aW9uIHVuY2VydGFpbnR5IChtKWB3aXRoIHRoZSBuZXcgdmFsdWVzDQogICMgaWYgVXBkYXRlRmxhZyA9IFRSVUUNCiAgbXV0YXRlKA0KICAgIExvbmdpdHVkZSA9IGlmX2Vsc2UoVXBkYXRlRmxhZywgTE9OR0lUVURFX2RlY2ltYWwsIExvbmdpdHVkZSksDQogICAgTGF0aXR1ZGUgPSBpZl9lbHNlKFVwZGF0ZUZsYWcsIExBVElUVURFX2RlY2ltYWwsIExhdGl0dWRlKSwNCiAgICBgTG9jYXRpb24gdW5jZXJ0YWludHkgKG0pYCA9IGlmX2Vsc2UoDQogICAgICBVcGRhdGVGbGFnLCBQUkVDSVNJT04sIGBMb2NhdGlvbiB1bmNlcnRhaW50eSAobSlgKQ0KICAgICkgJT4lDQogICMgVXBkYXRlIGNvbHVtbiBlZGl0X2Nvb3Jkc191bmMgdG8gbGFiZWwgZWRpdHMNCiAgbXV0YXRlKGVkaXRfY29vcmRzX3VuYyA9IGlmX2Vsc2UoVXBkYXRlRmxhZywgVFJVRSwgZWRpdF9jb29yZHNfdW5jKSkgJT4lDQogICMgUmVtb3ZlIHVubmVlZGVkIGNvbHVtbnMNCiAgc2VsZWN0KC1VcGRhdGVGbGFnLCAtTE9OR0lUVURFX2RlY2ltYWwsIC1MQVRJVFVERV9kZWNpbWFsLCAtUFJFQ0lTSU9OKQ0KYGBgDQoNCiMjIyMgQ1pfMDAxOV8wMTkNCg0KYGBge3J9DQppc3N1ZTNfQ1pfMDAxOSAlPiUgZmlsdGVyKFJTX0NPREUgPT0gIkNaXzAwMTlfMDE5IikNCm5yb3coZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogICAgICAgZmlsdGVyKFJTX0NPREUgPT0gIkNaXzAwMTlfMDE5IiAmIGNvb3JkaW5hdGVzX2VxdWFsID09IEZBTFNFICYNCiAgICAgICAgICAgICAgICBgVFYyIHJlbGV2w6kgbnVtYmVyYCA9PSA5MDczNTgpKQ0KYGBgDQoNClVwZGF0ZSBkYl9yZXN1cnZfdXBkYXRlZC4NCg0KYGBge3J9DQpkYl9yZXN1cnZfdXBkYXRlZCA8LSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgIyBDcmVhdGUgYW4gVXBkYXRlRmxhZyB0byBtYXJrIHJvd3MgdG8gdXBkYXRlDQogIG11dGF0ZShVcGRhdGVGbGFnID0gUlNfQ09ERSA9PSAiQ1pfMDAxOV8wMTkiICYgY29vcmRpbmF0ZXNfZXF1YWwgPT0gRkFMU0UgJg0KICAgICAgICAgICAgICAgIGBUVjIgcmVsZXbDqSBudW1iZXJgID09IDkwNzM1OCkgJT4lDQogICMgSm9pbiB3aXRoIGlzc3VlM19DWl8wMDE5IGJhc2VkIG9uIFJFTEVWRV9OUg0KICBsZWZ0X2pvaW4oaXNzdWUzX0NaXzAwMTkgJT4lDQogICAgICAgICAgICAgIHNlbGVjdChSRUxFVkVfTlIsIExPTkdJVFVERV9kZWNpbWFsLCBMQVRJVFVERV9kZWNpbWFsLA0KICAgICAgICAgICAgICAgICAgICAgUFJFQ0lTSU9OKSwNCiAgICAgICAgICAgIGpvaW5fYnkoYFRWMiByZWxldsOpIG51bWJlcmAgPT0gUkVMRVZFX05SKSkgJT4lDQogICMgVXBkYXRlIExvbmdpdHVkZSwgTGF0aXR1ZGUgYW5kIGBMb2NhdGlvbiB1bmNlcnRhaW50eSAobSlgd2l0aCB0aGUgbmV3IHZhbHVlcw0KICAjIGlmIFVwZGF0ZUZsYWcgPSBUUlVFDQogIG11dGF0ZSgNCiAgICBMb25naXR1ZGUgPSBpZl9lbHNlKFVwZGF0ZUZsYWcsIExPTkdJVFVERV9kZWNpbWFsLCBMb25naXR1ZGUpLA0KICAgIExhdGl0dWRlID0gaWZfZWxzZShVcGRhdGVGbGFnLCBMQVRJVFVERV9kZWNpbWFsLCBMYXRpdHVkZSksDQogICAgYExvY2F0aW9uIHVuY2VydGFpbnR5IChtKWAgPSBpZl9lbHNlKA0KICAgICAgVXBkYXRlRmxhZywgUFJFQ0lTSU9OLCBgTG9jYXRpb24gdW5jZXJ0YWludHkgKG0pYCkNCiAgICApICU+JQ0KICAjIFVwZGF0ZSBjb2x1bW4gZWRpdF9jb29yZHNfdW5jIHRvIGxhYmVsIGVkaXRzDQogIG11dGF0ZShlZGl0X2Nvb3Jkc191bmMgPSBpZl9lbHNlKFVwZGF0ZUZsYWcsIFRSVUUsIGVkaXRfY29vcmRzX3VuYykpICU+JQ0KICAjIFJlbW92ZSB1bm5lZWRlZCBjb2x1bW5zDQogIHNlbGVjdCgtVXBkYXRlRmxhZywgLUxPTkdJVFVERV9kZWNpbWFsLCAtTEFUSVRVREVfZGVjaW1hbCwgLVBSRUNJU0lPTikNCmBgYA0KDQojIyMjIENaXzAwMTlfMDM0DQoNCmBgYHtyfQ0KbnJvdyhpc3N1ZTNfQ1pfMDAxOSAlPiUgZmlsdGVyKFJTX0NPREUgPT0gIkNaXzAwMTlfMDM0IikpDQpucm93KGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAgICAgIGZpbHRlcihSU19DT0RFID09ICJDWl8wMDE5XzAzNCIpKQ0KYGBgDQoNClVwZGF0ZSBkYl9yZXN1cnZfdXBkYXRlZC4NCg0KYGBge3J9DQpkYl9yZXN1cnZfdXBkYXRlZCA8LSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgIyBDcmVhdGUgYW4gVXBkYXRlRmxhZyB0byBtYXJrIHJvd3MgdG8gdXBkYXRlDQogIG11dGF0ZShVcGRhdGVGbGFnID0gUlNfQ09ERSA9PSAiQ1pfMDAxOV8wMzQiKSAlPiUNCiAgIyBKb2luIHdpdGggaXNzdWUzX0NaXzAwMTkgYmFzZWQgb24gUkVMRVZFX05SDQogIGxlZnRfam9pbihpc3N1ZTNfQ1pfMDAxOSAlPiUNCiAgICAgICAgICAgICAgc2VsZWN0KFJFTEVWRV9OUiwgTE9OR0lUVURFX2RlY2ltYWwsIExBVElUVURFX2RlY2ltYWwsDQogICAgICAgICAgICAgICAgICAgICBQUkVDSVNJT04pLA0KICAgICAgICAgICAgam9pbl9ieShgVFYyIHJlbGV2w6kgbnVtYmVyYCA9PSBSRUxFVkVfTlIpKSAlPiUNCiAgIyBVcGRhdGUgTG9uZ2l0dWRlLCBMYXRpdHVkZSBhbmQgYExvY2F0aW9uIHVuY2VydGFpbnR5IChtKWB3aXRoIHRoZSBuZXcgdmFsdWVzDQogICMgaWYgVXBkYXRlRmxhZyA9IFRSVUUNCiAgbXV0YXRlKA0KICAgIExvbmdpdHVkZSA9IGlmX2Vsc2UoVXBkYXRlRmxhZywgTE9OR0lUVURFX2RlY2ltYWwsIExvbmdpdHVkZSksDQogICAgTGF0aXR1ZGUgPSBpZl9lbHNlKFVwZGF0ZUZsYWcsIExBVElUVURFX2RlY2ltYWwsIExhdGl0dWRlKSwNCiAgICBgTG9jYXRpb24gdW5jZXJ0YWludHkgKG0pYCA9IGlmX2Vsc2UoDQogICAgICBVcGRhdGVGbGFnLCBQUkVDSVNJT04sIGBMb2NhdGlvbiB1bmNlcnRhaW50eSAobSlgKQ0KICAgICkgJT4lDQogICMgVXBkYXRlIGNvbHVtbiBlZGl0X2Nvb3Jkc191bmMgdG8gbGFiZWwgZWRpdHMNCiAgbXV0YXRlKGVkaXRfY29vcmRzX3VuYyA9IGlmX2Vsc2UoVXBkYXRlRmxhZywgVFJVRSwgZWRpdF9jb29yZHNfdW5jKSkgJT4lDQogICMgUmVtb3ZlIHVubmVlZGVkIGNvbHVtbnMNCiAgc2VsZWN0KC1VcGRhdGVGbGFnLCAtTE9OR0lUVURFX2RlY2ltYWwsIC1MQVRJVFVERV9kZWNpbWFsLCAtUFJFQ0lTSU9OKQ0KYGBgDQoNCiMjIyMgQ1pfMDAxOV8wMzUNCg0KYGBge3J9DQpucm93KGlzc3VlM19DWl8wMDE5ICU+JSBmaWx0ZXIoUlNfQ09ERSA9PSAiQ1pfMDAxOV8wMzUiKSkNCm5yb3coZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogICAgICAgZmlsdGVyKFJTX0NPREUgPT0gIkNaXzAwMTlfMDM1IikpDQpgYGANCg0KVXBkYXRlIGRiX3Jlc3Vydl91cGRhdGVkLg0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkIDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAjIENyZWF0ZSBhbiBVcGRhdGVGbGFnIHRvIG1hcmsgcm93cyB0byB1cGRhdGUNCiAgbXV0YXRlKFVwZGF0ZUZsYWcgPSBSU19DT0RFID09ICJDWl8wMDE5XzAzNSIpICU+JQ0KICAjIEpvaW4gd2l0aCBpc3N1ZTNfQ1pfMDAxOSBiYXNlZCBvbiBSRUxFVkVfTlINCiAgbGVmdF9qb2luKGlzc3VlM19DWl8wMDE5ICU+JQ0KICAgICAgICAgICAgICBzZWxlY3QoUkVMRVZFX05SLCBMT05HSVRVREVfZGVjaW1hbCwgTEFUSVRVREVfZGVjaW1hbCwNCiAgICAgICAgICAgICAgICAgICAgIFBSRUNJU0lPTiksDQogICAgICAgICAgICBqb2luX2J5KGBUVjIgcmVsZXbDqSBudW1iZXJgID09IFJFTEVWRV9OUikpICU+JQ0KICAjIFVwZGF0ZSBMb25naXR1ZGUsIExhdGl0dWRlIGFuZCBgTG9jYXRpb24gdW5jZXJ0YWludHkgKG0pYHdpdGggdGhlIG5ldyB2YWx1ZXMNCiAgIyBpZiBVcGRhdGVGbGFnID0gVFJVRQ0KICBtdXRhdGUoDQogICAgTG9uZ2l0dWRlID0gaWZfZWxzZShVcGRhdGVGbGFnLCBMT05HSVRVREVfZGVjaW1hbCwgTG9uZ2l0dWRlKSwNCiAgICBMYXRpdHVkZSA9IGlmX2Vsc2UoVXBkYXRlRmxhZywgTEFUSVRVREVfZGVjaW1hbCwgTGF0aXR1ZGUpLA0KICAgIGBMb2NhdGlvbiB1bmNlcnRhaW50eSAobSlgID0gaWZfZWxzZSgNCiAgICAgIFVwZGF0ZUZsYWcsIFBSRUNJU0lPTiwgYExvY2F0aW9uIHVuY2VydGFpbnR5IChtKWApDQogICAgKSAlPiUNCiAgIyBVcGRhdGUgY29sdW1uIGVkaXRfY29vcmRzX3VuYyB0byBsYWJlbCBlZGl0cw0KICBtdXRhdGUoZWRpdF9jb29yZHNfdW5jID0gaWZfZWxzZShVcGRhdGVGbGFnLCBUUlVFLCBlZGl0X2Nvb3Jkc191bmMpKSAlPiUNCiAgIyBSZW1vdmUgdW5uZWVkZWQgY29sdW1ucw0KICBzZWxlY3QoLVVwZGF0ZUZsYWcsIC1MT05HSVRVREVfZGVjaW1hbCwgLUxBVElUVURFX2RlY2ltYWwsIC1QUkVDSVNJT04pDQpgYGANCg0KIyMjIyBDWl8wMDE5XzA0MQ0KDQpSZWFkIHRoZSBmaWxlIGluIHRoZSByYXcgZGF0YSBmb2xkZXIuDQoNCmBgYHtyfQ0KaXNzdWUzX0NaXzAwMTlfMDQxIDwtIHJlYWRfZXhjZWwoDQogIGhlcmUoImRhdGEiLCAicmF3IiwgIkRhdGFfY29ycmVjdGlvbnNfSWxvbmEiLCAiSXNzdWUzIiwNCiAgICAgICAiQ1pfMDAxOV8wNDFfY29ycmVjdGVkLnhsc3giKSkNCmBgYA0KDQpOZWVkIHRvIHVwZGF0ZSBMb25naXR1ZGUsIExhdGl0dWRlIGFuZCBMb2NhdGlvbiB1bmNlcnRhaW50eSAobSkgKEkgZ3Vlc3MgdGhpcyBpcyBjb2RlZCBhcyBQUkVDSVNJT04pLg0KDQpJbG9uYTogUGxlYXNlIG1pbmQgdGhhdCBDWiBmaWxlcyAoYWxsIHJlc3VydmV5IGRhdGFzZXRzIHdpdGggcnNfY29kZXMgdGhhdCBiZWdpbnMgd2l0aCDigJxDWuKAnSBoYXZlIHNwZWNpYWwgZm9ybWF0IG9mIGNvb3JkaW5hdGVzIG5vdCBkZWNpbWFsIGRlZ3JlZXMgYnV0IERETU1TUy5TUykuDQoNCldlIG5lZWQgdG8gY29udmVydCBjb29yZGluYXRlcyB0byBkZWNpbWFsIGRlZ3JlZXMuDQoNCkFwcGx5IHRoZSBjb252ZXJzaW9uIGZ1bmN0aW9uIHRvIHRoZSBsb25naXR1ZGUgYW5kIGxhdGl0dWRlIGNvbHVtbnM6DQoNCmBgYHtyfQ0KaXNzdWUzX0NaXzAwMTlfMDQxIDwtIGlzc3VlM19DWl8wMDE5XzA0MSAlPiUNCiAgbXV0YXRlKExPTkdJVFVERV9kZWNpbWFsID0gc2FwcGx5KExPTkdJVFVERSwgY29udmVydF90b19kZWNpbWFsKSwNCiAgICAgICAgIExBVElUVURFX2RlY2ltYWwgPSBzYXBwbHkoTEFUSVRVREUsIGNvbnZlcnRfdG9fZGVjaW1hbCkpDQpgYGANCg0KYGBge3J9DQpucm93KGlzc3VlM19DWl8wMDE5XzA0MSkNCm5yb3coZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogICAgICAgZmlsdGVyKFJTX0NPREUgPT0gIkNaXzAwMTlfMDQxIiAmIGNvb3JkaW5hdGVzX2VxdWFsID09IEZBTFNFKSkNCmBgYA0KDQpVcGRhdGUgZGJfcmVzdXJ2X3VwZGF0ZWQuDQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X3VwZGF0ZWQgPC0gZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogICMgQ3JlYXRlIGFuIFVwZGF0ZUZsYWcgdG8gbWFyayByb3dzIHRvIHVwZGF0ZQ0KICBtdXRhdGUoVXBkYXRlRmxhZyA9IFJTX0NPREUgPT0gIkNaXzAwMTlfMDQxIiAmIGNvb3JkaW5hdGVzX2VxdWFsID09IEZBTFNFKSAlPiUNCiAgIyBKb2luIHdpdGggaXNzdWUzX0NaXzAwMTlfMDQxIGJhc2VkIG9uIFJFTEVWRV9OUg0KICBsZWZ0X2pvaW4oaXNzdWUzX0NaXzAwMTlfMDQxICU+JQ0KICAgICAgICAgICAgICBzZWxlY3QoUkVMRVZFX05SLCBMT05HSVRVREVfZGVjaW1hbCwgTEFUSVRVREVfZGVjaW1hbCwNCiAgICAgICAgICAgICAgICAgICAgIFBSRUNJU0lPTiksDQogICAgICAgICAgICBqb2luX2J5KGBUVjIgcmVsZXbDqSBudW1iZXJgID09IFJFTEVWRV9OUikpICU+JQ0KICAjIFVwZGF0ZSBMb25naXR1ZGUsIExhdGl0dWRlIGFuZCBgTG9jYXRpb24gdW5jZXJ0YWludHkgKG0pYHdpdGggdGhlIG5ldyB2YWx1ZXMNCiAgIyBpZiBVcGRhdGVGbGFnID0gVFJVRQ0KICBtdXRhdGUoDQogICAgTG9uZ2l0dWRlID0gaWZfZWxzZShVcGRhdGVGbGFnLCBMT05HSVRVREVfZGVjaW1hbCwgTG9uZ2l0dWRlKSwNCiAgICBMYXRpdHVkZSA9IGlmX2Vsc2UoVXBkYXRlRmxhZywgTEFUSVRVREVfZGVjaW1hbCwgTGF0aXR1ZGUpLA0KICAgIGBMb2NhdGlvbiB1bmNlcnRhaW50eSAobSlgID0gaWZfZWxzZSgNCiAgICAgIFVwZGF0ZUZsYWcsIFBSRUNJU0lPTiwgYExvY2F0aW9uIHVuY2VydGFpbnR5IChtKWApDQogICAgKSAlPiUNCiAgIyBVcGRhdGUgY29sdW1uIGVkaXRfY29vcmRzX3VuYyB0byBsYWJlbCBlZGl0cw0KICBtdXRhdGUoZWRpdF9jb29yZHNfdW5jID0gaWZfZWxzZShVcGRhdGVGbGFnLCBUUlVFLCBlZGl0X2Nvb3Jkc191bmMpKSAlPiUNCiAgIyBSZW1vdmUgdW5uZWVkZWQgY29sdW1ucw0KICBzZWxlY3QoLVVwZGF0ZUZsYWcsIC1MT05HSVRVREVfZGVjaW1hbCwgLUxBVElUVURFX2RlY2ltYWwsIC1QUkVDSVNJT04pDQpgYGANCg0KIyMjIyBDWl8wMDE5XzA0OA0KDQpSZWFkIHRoZSBmaWxlIGluIHRoZSByYXcgZGF0YSBmb2xkZXIuDQoNCmBgYHtyfQ0KaXNzdWUzX0NaXzAwMTlfMDQ4IDwtIHJlYWRfZXhjZWwoDQogIGhlcmUoImRhdGEiLCAicmF3IiwgIkRhdGFfY29ycmVjdGlvbnNfSWxvbmEiLCAiSXNzdWUzIiwNCiAgICAgICAiQ1pfMDAxOV8wNDhfY29ycmVjdGVkLnhsc3giKSkNCmBgYA0KDQpOZWVkIHRvIHVwZGF0ZSBMb25naXR1ZGUsIExhdGl0dWRlIGFuZCBMb2NhdGlvbiB1bmNlcnRhaW50eSAobSkgKEkgZ3Vlc3MgdGhpcyBpcyBjb2RlZCBhcyBQUkVDSVNJT04pLg0KDQpJbG9uYTogUGxlYXNlIG1pbmQgdGhhdCBDWiBmaWxlcyAoYWxsIHJlc3VydmV5IGRhdGFzZXRzIHdpdGggcnNfY29kZXMgdGhhdCBiZWdpbnMgd2l0aCDigJxDWuKAnSBoYXZlIHNwZWNpYWwgZm9ybWF0IG9mIGNvb3JkaW5hdGVzIG5vdCBkZWNpbWFsIGRlZ3JlZXMgYnV0IERETU1TUy5TUykuDQoNCldlIG5lZWQgdG8gY29udmVydCBjb29yZGluYXRlcyB0byBkZWNpbWFsIGRlZ3JlZXMuDQoNCkFwcGx5IHRoZSBjb252ZXJzaW9uIGZ1bmN0aW9uIHRvIHRoZSBsb25naXR1ZGUgYW5kIGxhdGl0dWRlIGNvbHVtbnM6DQoNCmBgYHtyfQ0KaXNzdWUzX0NaXzAwMTlfMDQ4IDwtIGlzc3VlM19DWl8wMDE5XzA0OCAlPiUNCiAgbXV0YXRlKExPTkdJVFVERV9kZWNpbWFsID0gc2FwcGx5KExPTkdJVFVERSwgY29udmVydF90b19kZWNpbWFsKSwNCiAgICAgICAgIExBVElUVURFX2RlY2ltYWwgPSBzYXBwbHkoTEFUSVRVREUsIGNvbnZlcnRfdG9fZGVjaW1hbCkpDQpgYGANCg0KYGBge3J9DQpucm93KGlzc3VlM19DWl8wMDE5XzA0OCkNCm5yb3coZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogICAgICAgZmlsdGVyKFJTX0NPREUgPT0gIkNaXzAwMTlfMDQ4IiAmIGNvb3JkaW5hdGVzX2VxdWFsID09IEZBTFNFKSkNCmBgYA0KDQpVcGRhdGUgZGJfcmVzdXJ2X3VwZGF0ZWQuDQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X3VwZGF0ZWQgPC0gZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogICMgQ3JlYXRlIGFuIFVwZGF0ZUZsYWcgdG8gbWFyayByb3dzIHRvIHVwZGF0ZQ0KICBtdXRhdGUoVXBkYXRlRmxhZyA9IFJTX0NPREUgPT0gIkNaXzAwMTlfMDQ4IiAmIGNvb3JkaW5hdGVzX2VxdWFsID09IEZBTFNFKSAlPiUNCiAgIyBKb2luIHdpdGggaXNzdWUzX0NaXzAwMTlfMDQ4IGJhc2VkIG9uIFBsb3RPYnNlcnZhdGlvbklEDQogIGxlZnRfam9pbihpc3N1ZTNfQ1pfMDAxOV8wNDggJT4lDQogICAgICAgICAgICAgIHNlbGVjdChQbG90T2JzZXJ2YXRpb25JRCwgTE9OR0lUVURFX2RlY2ltYWwsIExBVElUVURFX2RlY2ltYWwsDQogICAgICAgICAgICAgICAgICAgICBQUkVDSVNJT04pKSAlPiUNCiAgIyBVcGRhdGUgTG9uZ2l0dWRlLCBMYXRpdHVkZSBhbmQgYExvY2F0aW9uIHVuY2VydGFpbnR5IChtKWB3aXRoIHRoZSBuZXcgdmFsdWVzDQogICMgaWYgVXBkYXRlRmxhZyA9IFRSVUUNCiAgbXV0YXRlKA0KICAgIExvbmdpdHVkZSA9IGlmX2Vsc2UoVXBkYXRlRmxhZywgTE9OR0lUVURFX2RlY2ltYWwsIExvbmdpdHVkZSksDQogICAgTGF0aXR1ZGUgPSBpZl9lbHNlKFVwZGF0ZUZsYWcsIExBVElUVURFX2RlY2ltYWwsIExhdGl0dWRlKSwNCiAgICBgTG9jYXRpb24gdW5jZXJ0YWludHkgKG0pYCA9IGlmX2Vsc2UoDQogICAgICBVcGRhdGVGbGFnLCBQUkVDSVNJT04sIGBMb2NhdGlvbiB1bmNlcnRhaW50eSAobSlgKQ0KICAgICkgJT4lDQogICMgVXBkYXRlIGNvbHVtbiBlZGl0X2Nvb3Jkc191bmMgdG8gbGFiZWwgZWRpdHMNCiAgbXV0YXRlKGVkaXRfY29vcmRzX3VuYyA9IGlmX2Vsc2UoVXBkYXRlRmxhZywgVFJVRSwgZWRpdF9jb29yZHNfdW5jKSkgJT4lDQogICMgUmVtb3ZlIHVubmVlZGVkIGNvbHVtbnMNCiAgc2VsZWN0KC1VcGRhdGVGbGFnLCAtTE9OR0lUVURFX2RlY2ltYWwsIC1MQVRJVFVERV9kZWNpbWFsLCAtUFJFQ0lTSU9OKQ0KYGBgDQoNCiMjIyBFU18wMDAzDQoNClJlYWQgdGhlIGZpbGUgaW4gdGhlIHJhdyBkYXRhIGZvbGRlci4NCg0KYGBge3J9DQppc3N1ZTNfRVNfMDAwMyA8LSByZWFkX2V4Y2VsKA0KICBoZXJlKCJkYXRhIiwgInJhdyIsICJEYXRhX2NvcnJlY3Rpb25zX0lsb25hIiwgIklzc3VlMyIsDQogICAgICAgIklzc3VlM19FU18wMDAzX2NvcnJlY3RlZC54bHN4IikpDQpgYGANCg0KYGBge3J9DQpucm93KGlzc3VlM19FU18wMDAzKQ0KbnJvdyhkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgICAgICBmaWx0ZXIoUlNfQ09ERSA9PSAiRVNfMDAwMyIgJiBjb29yZGluYXRlc19lcXVhbCA9PSBGQUxTRSkpDQpgYGANCg0KVXBkYXRlIGRiX3Jlc3Vydl91cGRhdGVkLg0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkIDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAjIENyZWF0ZSBhbiBVcGRhdGVGbGFnIHRvIG1hcmsgcm93cyB0byB1cGRhdGUNCiAgbXV0YXRlKFVwZGF0ZUZsYWcgPSBSU19DT0RFID09ICJFU18wMDAzIiAmIGNvb3JkaW5hdGVzX2VxdWFsID09IEZBTFNFKSAlPiUNCiAgIyBKb2luIHdpdGggaXNzdWUzX0VTXzAwMDMgYmFzZWQgb24gUGxvdE9ic2VydmF0aW9uSUQNCiAgbGVmdF9qb2luKGlzc3VlM19FU18wMDAzICU+JQ0KICAgICAgICAgICAgICBzZWxlY3QoUGxvdE9ic2VydmF0aW9uSUQsIExPTkdJVFVERSwgTEFUSVRVREUsDQogICAgICAgICAgICAgICAgICAgICBQUkVDSVNJT04pKSAlPiUNCiAgIyBVcGRhdGUgTG9uZ2l0dWRlLCBMYXRpdHVkZSBhbmQgYExvY2F0aW9uIHVuY2VydGFpbnR5IChtKWB3aXRoIHRoZSBuZXcgdmFsdWVzDQogICMgaWYgVXBkYXRlRmxhZyA9IFRSVUUNCiAgbXV0YXRlKA0KICAgIExvbmdpdHVkZSA9IGlmX2Vsc2UoVXBkYXRlRmxhZywgTE9OR0lUVURFLCBMb25naXR1ZGUpLA0KICAgIExhdGl0dWRlID0gaWZfZWxzZShVcGRhdGVGbGFnLCBMQVRJVFVERSwgTGF0aXR1ZGUpLA0KICAgIGBMb2NhdGlvbiB1bmNlcnRhaW50eSAobSlgID0gaWZfZWxzZSgNCiAgICAgIFVwZGF0ZUZsYWcsIFBSRUNJU0lPTiwgYExvY2F0aW9uIHVuY2VydGFpbnR5IChtKWApDQogICAgKSAlPiUNCiAgIyBVcGRhdGUgY29sdW1uIGVkaXRfY29vcmRzX3VuYyB0byBsYWJlbCBlZGl0cw0KICBtdXRhdGUoZWRpdF9jb29yZHNfdW5jID0gaWZfZWxzZShVcGRhdGVGbGFnLCBUUlVFLCBlZGl0X2Nvb3Jkc191bmMpKSAlPiUNCiAgIyBSZW1vdmUgdW5uZWVkZWQgY29sdW1ucw0KICBzZWxlY3QoLVVwZGF0ZUZsYWcsIC1MT05HSVRVREUsIC1MQVRJVFVERSwgLVBSRUNJU0lPTikNCmBgYA0KDQojIyMgUExfMDAwOQ0KDQpSZWFkIHRoZSBmaWxlIGluIHRoZSByYXcgZGF0YSBmb2xkZXIgKEkgbWFudWFsbHkgY29ycmVjdGVkIHRoZSBjb29yZGlhbnRlcyBvZiB0aGUgbGFzdCA0IHJvd3Mgb2YgdGhpcyBmaWxlIGJlY2F1c2UgbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSB3ZXJlIG14aWVkIC0gSWxvbmEgc2VudCBhbiBlbWFpbCBhYm91dCB0aGlzKS4NCg0KYGBge3J9DQppc3N1ZTNfUExfMDAwOSA8LSByZWFkX2V4Y2VsKA0KICBoZXJlKCJkYXRhIiwgImVkaXRlZCIsICJJc3N1ZTNfUExfMDAwOV9jb29yZWN0ZWRfY29yckFWLnhsc3giKSkNCmBgYA0KDQpgYGB7cn0NCm5yb3coaXNzdWUzX1BMXzAwMDkpDQpucm93KGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAgICAgIGZpbHRlcihSU19DT0RFID09ICJQTF8wMDA5IiAmIGNvb3JkaW5hdGVzX2NvbnNpc3RlbnQgPT0gRkFMU0UpKQ0KYGBgDQoNCklsb25hJ3MgZW1haWw6IEkgYW0gc2VuZGluZyB5b3UgY29ycmVjdGVkIGNvb3JkaW5hdGUgZm9yIFBMXzAwMDkg4oCTIG9uZSBjb29yZGluYXRlIGFuZCBjb2Rpbmcgb2YgMSBwbG90IOKAkyBjaGFuZ2VzIGluIHJlZC4NCg0KQWNjb3JkaW5nIHRvIGluZm8gaW4gRXhjZWwgZmlsZSAoKSwgbmVlZCB0byB1cGRhdGUgTG9uZ2l0dWRlLCBMYXRpdHVkZSBhbmQgUmVTdXJ2ZXkgcGxvdC4gDQoNClVwZGF0ZSBkYl9yZXN1cnZfdXBkYXRlZC4NCg0KYGBge3J9DQpkYl9yZXN1cnZfdXBkYXRlZCA8LSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgIyBDcmVhdGUgYW4gVXBkYXRlRmxhZyB0byBtYXJrIHJvd3MgdG8gdXBkYXRlDQogIG11dGF0ZShVcGRhdGVGbGFnID0gUlNfQ09ERSA9PSAiUExfMDAwOSIgJiBjb29yZGluYXRlc19jb25zaXN0ZW50ID09IEZBTFNFKSAlPiUNCiAgIyBKb2luIHdpdGggaXNzdWUzX1BMXzAwMDkgYmFzZWQgb24gUGxvdE9ic2VydmF0aW9uSUQNCiAgbGVmdF9qb2luKGlzc3VlM19QTF8wMDA5ICU+JQ0KICAgICAgICAgICAgICBzZWxlY3QoUGxvdE9ic2VydmF0aW9uSUQsIGBSZVN1cnZleSBwbG90YCwgTG9uZ2l0dWRlLCBMYXRpdHVkZSkgJT4lDQogICAgICAgICAgICAgICMgUmVuYW1lIGNvbHVtbnMgYFJlc3VydmV5IHBsb3RgLCBMb25naXR1ZGUgYW5kIExhdGl0dWRlDQogICAgICAgICAgICAgICMgdG8gYXZvaWQgam9pbmluZyBvbiB0aGVzZSBjb2x1bW5zDQogICAgICAgICAgICAgIHJlbmFtZShgUmVTdXJ2ZXkgcGxvdC5uZXdgID0gYFJlU3VydmV5IHBsb3RgLA0KICAgICAgICAgICAgICAgICAgICAgTG9uZ2l0dWRlLm5ldyA9IExvbmdpdHVkZSwgTGF0aXR1ZGUubmV3ID0gTGF0aXR1ZGUpKSAlPiUNCiAgIyBVcGRhdGUgTG9uZ2l0dWRlLCBMYXRpdHVkZSBhbmQgYFJlU3VydmV5IHBsb3Rgd2l0aCB0aGUgbmV3IHZhbHVlcw0KICAjIGlmIFVwZGF0ZUZsYWcgPSBUUlVFDQogIG11dGF0ZSgNCiAgICBMb25naXR1ZGUgPSBpZl9lbHNlKFVwZGF0ZUZsYWcsIExvbmdpdHVkZS5uZXcsIExvbmdpdHVkZSksDQogICAgTGF0aXR1ZGUgPSBpZl9lbHNlKFVwZGF0ZUZsYWcsIExhdGl0dWRlLm5ldywgTGF0aXR1ZGUpLA0KICAgIGBSZVN1cnZleSBwbG90YCA9IGlmX2Vsc2UoDQogICAgICBVcGRhdGVGbGFnLCBgUmVTdXJ2ZXkgcGxvdC5uZXdgLCBgUmVTdXJ2ZXkgcGxvdGApDQogICAgKSAlPiUNCiAgIyBVcGRhdGUgY29sdW1uIGVkaXRfY29vcmRzIHRvIGxhYmVsIGVkaXRzDQogIG11dGF0ZShlZGl0X2Nvb3JkcyA9IGlmX2Vsc2UoVXBkYXRlRmxhZywgVFJVRSwgZWRpdF9jb29yZHMpKSAlPiUNCiAgIyBVcGRhdGUgY29sdW1uIGVkaXRfcHBsb3QgdG8gbGFiZWwgZWRpdHMNCiAgbXV0YXRlKGVkaXRfcGxvdCA9IGlmX2Vsc2UoVXBkYXRlRmxhZywgVFJVRSwgZWRpdF9wbG90KSkgJT4lDQogICMgUmVtb3ZlIHVubmVlZGVkIGNvbHVtbnMNCiAgc2VsZWN0KC1VcGRhdGVGbGFnLCAtTG9uZ2l0dWRlLm5ldywgLUxhdGl0dWRlLm5ldywgLWBSZVN1cnZleSBwbG90Lm5ld2ApDQpgYGANCg0KIyMjIElUXzAwMDQNCg0KVGV4dCBpbiBJbG9uYSdzIGVtYWlsOiBPbmUgY29ycmVjdGlvbiBmb3IgaXNzdWU0IOKAkyA2IGNvb3JkaW5hdGVzIGFyZSBjb3JyZWN0ZWQg4oCTIGhvd2V2ZXIgdGhpcyBEQiDigJMgaGFzIGFub3RoZXIgcHJvYmxlbSB3aXRoIHJzX2NvZGVzIChzZWVtcyB0byBtZSB0aGF0IG9yaWdpbmFsIGNvZGluZyBvZiBwbG90cyBhcmUgd3Jvbmcg4oCTIHRoYXTCtHMgd2h5IHlvdSBoYXZlIG5vdCByZXBlYXRpbmcgcGxvdHMpIGFuZCBhbHNvIG9uZSBncm91cCBvZiBvYnNlcnZhdGlvbiBoYXMgYWxzbyBkdWJpb3VzIGNvb3JkaW5hdGVzLiBJIGFtIHdhaXRpbmcgZm9yIGFub3RoZXIgY29ycmVjdGlvbnMuDQoNCmBgYHtyfQ0KSXNzdWUzX0lUXzAwMDQgPC0gcmVhZF9leGNlbCgNCiAgaGVyZSgiZGF0YSIsICJyYXciLCAiRGF0YV9jb3JyZWN0aW9uc19JbG9uYSIsICJJc3N1ZTMiLA0KICAgICAgICJJc3N1ZTNfSVRfMDAwNF9jb3JlY3RlZC54bHN4IikpDQpgYGANCg0KYGBge3J9DQpkYl9yZXN1cnZfdXBkYXRlZCA8LSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgIyBDcmVhdGUgYW4gVXBkYXRlRmxhZyB0byBtYXJrIHJvd3MgdG8gdXBkYXRlDQogIG11dGF0ZShVcGRhdGVGbGFnID0gUGxvdE9ic2VydmF0aW9uSUQgPT0gMzM5NDQyIHwNCiAgICAgICAgICAgUGxvdE9ic2VydmF0aW9uSUQgPT0gMzM5NDQzIHwgUGxvdE9ic2VydmF0aW9uSUQgPT0zMzk0NDEpICU+JQ0KICAjIEpvaW4gd2l0aCBJc3N1ZTNfSVRfMDAwNCBiYXNlZCBvbiBQbG90T2JzZXJ2YXRpb25JRA0KICBsZWZ0X2pvaW4oSXNzdWUzX0lUXzAwMDQgJT4lDQogICAgICAgICAgICAgIG11dGF0ZShMb25naXR1ZGVfbmV3ID0gTG9uZ2l0dWRlLCBMYXRpdHVkZV9uZXcgPSBMYXRpdHVkZSkgJT4lDQogICAgICAgICAgICAgIHNlbGVjdChQbG90T2JzZXJ2YXRpb25JRCwgTG9uZ2l0dWRlX25ldywgTGF0aXR1ZGVfbmV3KSkgJT4lDQogICMgVXBkYXRlIExvbmdpdHVkZSBhbmQgTGF0aXR1ZGUgd2l0aCB0aGUgbmV3IHZhbHVlcw0KICAjIGlmIFVwZGF0ZUZsYWcgPSBUUlVFDQogIG11dGF0ZSgNCiAgICBMb25naXR1ZGUgPSBpZl9lbHNlKFVwZGF0ZUZsYWcsIExvbmdpdHVkZV9uZXcsIExvbmdpdHVkZSksDQogICAgTGF0aXR1ZGUgPSBpZl9lbHNlKFVwZGF0ZUZsYWcsIExhdGl0dWRlX25ldywgTGF0aXR1ZGUpDQogICAgKSAlPiUNCiAgIyBVcGRhdGUgY29sdW1uIGVkaXRfY29vcmRzIHRvIGxhYmVsIGVkaXRzDQogIG11dGF0ZShlZGl0X2Nvb3JkcyA9IGlmX2Vsc2UoVXBkYXRlRmxhZywgVFJVRSwgZWRpdF9jb29yZHMpKSAlPiUNCiAgIyBSZW1vdmUgdW5uZWVkZWQgY29sdW1ucw0KICBzZWxlY3QoLVVwZGF0ZUZsYWcsIC1Mb25naXR1ZGVfbmV3LCAtTGF0aXR1ZGVfbmV3KQ0KYGBgDQoNCiMjIyBDb3JyZWN0aW9uIGNvbmZpcm1hdGlvbg0KDQpDb25maXJtIHRoYXQgYWxsIHRoYXQgbmVlZGVkIHRvIGJlIGNvcnJlY3RlZCBoYXZlIGJlZW4gY29ycmVjdGVkOg0KDQpgYGB7cn0NCmFsbC5lcXVhbCgNCiAgZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lIGZpbHRlcihjb3JyZWN0ID09ICJ0b19jb3JyZWN0IikgJT4lDQogICAgZGlzdGluY3QoUlNfQ09ERSwgRGF0YXNldCwgYFJlU3VydmV5IHByb2plY3RgKSAlPiUgYXJyYW5nZShSU19DT0RFKSwNCiAgZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lIGZpbHRlcihjb3JyZWN0ID09ICJ0b19jb3JyZWN0IikgJT4lDQogICAgZGlzdGluY3QoUlNfQ09ERSwgRGF0YXNldCwgYFJlU3VydmV5IHByb2plY3RgKSAlPiUgYXJyYW5nZShSU19DT0RFKQ0KICApDQpgYGANCg0KIyMgUmVtb3ZlIHVubmVlZGVkIGNvbHVtbiBhbmQgcmVjYWxjdWxhdGUgc29tZSB2YXJpYWJsZXMNCg0KYGBge3J9DQpkYl9yZXN1cnZfdXBkYXRlZCA8LSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgIyBSZW1vdmUgImNvcnJlY3QiIGNvbHVtbiAobm90IG5lZWRlZCBhbnltb3JlKQ0KICBzZWxlY3QoLWNvcnJlY3QpICU+JQ0KICAjIFJlY2FsY3VsYXRlIHVwZGF0ZWQgY29vcmRpbmF0ZXMNCiAgbXV0YXRlKExvbl91cGRhdGVkID0gaWZlbHNlKGlzLm5hKExvbl9wcmVjKSwgTG9uZ2l0dWRlLCBMb25fcHJlYyksDQogICAgICAgICBMYXRfdXBkYXRlZCA9IGlmZWxzZShpcy5uYShMYXRfcHJlYyksIExhdGl0dWRlLCBMYXRfcHJlYykpICU+JQ0KICAjIFJlY2FsY3VsYXRlIGNvb3JkaW5hdGVzX2VxdWFsIGFuZCBjb29yZGluYXRlc19jb25zaXN0ZW50DQogIGdyb3VwX2J5KFJTX0NPREUsIGBSZVN1cnZleSBzaXRlYCwgYFJlU3VydmV5IHBsb3RgKSAlPiUNCiAgbXV0YXRlKA0KICAgIGxvbl9yYW5nZSA9IGlmZWxzZShhbGwoaXMubmEoTG9uX3VwZGF0ZWQpKSwgTkEsDQogICAgICAgICAgICAgICAgICAgICAgIG1heChMb25fdXBkYXRlZCwgbmEucm0gPSBUKSAtIA0KICAgICAgICAgICAgICAgICAgICAgICAgIG1pbihMb25fdXBkYXRlZCwgbmEucm0gPSBUKSksDQogICAgbGF0X3JhbmdlID0gaWZlbHNlKGFsbChpcy5uYShMYXRfdXBkYXRlZCkpLCBOQSwNCiAgICAgICAgICAgICAgICAgICAgICAgbWF4KExhdF91cGRhdGVkLCBuYS5ybSA9IFQpIC0gDQogICAgICAgICAgICAgICAgICAgICAgICAgbWluKExhdF91cGRhdGVkLCBuYS5ybSA9IFQpKSwNCiAgICBjb29yZGluYXRlc19lcXVhbCA9IGlmZWxzZShpcy5uYShMb25fdXBkYXRlZCkgJiBpcy5uYShMYXRfdXBkYXRlZCksIE5BLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvbl9yYW5nZSA9PSAwICYgbGF0X3JhbmdlID09IDApLA0KICAgIGNvb3JkaW5hdGVzX2NvbnNpc3RlbnQgPSBpZmVsc2UoaXMubmEoTG9uX3VwZGF0ZWQpICYgaXMubmEoTGF0X3VwZGF0ZWQpLCBOQSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvbl9yYW5nZSA8IHRocmVzaG9sZCAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhdF9yYW5nZSA8IHRocmVzaG9sZCkNCiAgICApICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIHNlbGVjdCgtbG9uX3JhbmdlLCAtbGF0X3JhbmdlKQ0KYGBgDQoNCiMjIFVwZGF0ZWQgYmFycGxvdCBvZiBjb29yZGluYXRlIHN0YXR1cw0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkICU+JSANCiAgZ3JvdXBfYnkoUlNfQ09ERSxgUmVTdXJ2ZXkgc2l0ZWAsIGBSZVN1cnZleSBwbG90YCkgJT4lDQogIHN1bW1hcml6ZShpc19lcXVhbCA9IGFsbChjb29yZGluYXRlc19lcXVhbCksDQogICAgICAgICAgICBpc19jb25zaXN0ZW50ID0gYWxsKGNvb3JkaW5hdGVzX2NvbnNpc3RlbnQpLA0KICAgICAgICAgICAgLmdyb3VwcyA9ICJkcm9wIikgJT4lDQogIG11dGF0ZShjb29yZGluYXRlX3N0YXR1cyA9IGNhc2Vfd2hlbigNCiAgICBpc19lcXVhbCB+ICJFcXVhbCIsDQogICAgIWlzX2VxdWFsICYgaXNfY29uc2lzdGVudCB+ICJDb25zaXN0ZW50ICg8IDAuMDAxwropIiwNCiAgICAhaXNfZXF1YWwgJiAhaXNfY29uc2lzdGVudCB+ICJJbmNvbnNpc3RlbnQgKD4gMC4wMDHCuikiKSkgJT4lDQogIGNvdW50KGNvb3JkaW5hdGVfc3RhdHVzKSU+JQ0KICBtdXRhdGUocGVyY2VudGFnZSA9IG4gLyBzdW0obikgKiAxMDApICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBwZXJjZW50YWdlLCB5ID0gY29vcmRpbmF0ZV9zdGF0dXMsIGZpbGwgPSBjb29yZGluYXRlX3N0YXR1cykpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsgDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYXN0ZTAocm91bmQocGVyY2VudGFnZSwgMSksICIlIikpLA0KICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSksIHNpemUgPSAzKSArIA0KICBsYWJzKHggPSAiUGVyY2VudGFnZSBvZiBQbG90cyIsIHkgPSBOVUxMKSArDQogIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMikpICsNCiAgY29vcmRfZmxpcCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KYGBgDQoNCiMgQ29ycmVjdGlvbiBJU1NVRSA0DQoNCiMjIENvdW50IHJlc3VydmV5cw0KDQpgYGB7cn0NCmNvdW50X3Jlc3VydmV5cyA8LSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgIyBDb252ZXJ0IGRhdGVzIHRvIGRhdGUgZm9ybWF0IGFuZCBnZXQgdGhlIHllYXINCiAgbXV0YXRlKGRhdGUgPSBkbXkoYERhdGUgb2YgcmVjb3JkaW5nYCksIHllYXIgPSB5ZWFyKGRhdGUpKSAlPiUNCiAjIEdyb3VwIGJ5IFJTX0NPREUsIGBSZVN1cnZleSBzaXRlYCwgYFJlU3VydmV5IHBsb3RgDQogIGdyb3VwX2J5KFJTX0NPREUsIGBSZVN1cnZleSBzaXRlYCwgYFJlU3VydmV5IHBsb3RgKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgICMgR2V0IGhvdyBtYW55IGRpZmZlcmVudCB5ZWFycyBmb3IgZWFjaCB1bmlxdWUgZ3JvdXANCiAgICBkaXN0aW5jdF95ZWFycz1uX2Rpc3RpbmN0KHllYXIpLCANCiAgICAjIEdldCBob3cgbWFueSBkaWZmZXJlbnQgZGF0ZXMgZm9yIGVhY2ggdW5pcXVlIGdyb3VwDQogICAgZGlzdGluY3RfZGF0ZXM9bl9kaXN0aW5jdChkYXRlKSwgLmdyb3VwcyA9ICJkcm9wIikNCmBgYA0KDQpTdW1tYXJ5IHN0YXRzOg0KDQpgYGB7cn0NCnN1bW1hcnkoY291bnRfcmVzdXJ2ZXlzJGRpc3RpbmN0X3llYXJzKQ0Kc2QoY291bnRfcmVzdXJ2ZXlzJGRpc3RpbmN0X3llYXJzKQ0KYGBgDQoNCkhpc3RvZ3JhbXM6DQoNCmBgYHtyfQ0KIyBGb3IgYWxsIGRhdGENCmdncGxvdChjb3VudF9yZXN1cnZleXMsIGFlcyh4ID0gZGlzdGluY3RfeWVhcnMpKSArIA0KICBnZW9tX2hpc3RvZ3JhbShmaWxsID0gIndoaXRlIiwgY29sb3IgPSAiYmxhY2siLCBiaW5zID0gNTUpKw0KICB4bGFiKCJOdW1iZXIgb2YgUmVTdXJ2ZXkgb2JzZXJ2YXRpb25zIChkaWZmZXJlbnQgeWVhcnMpIikgKw0KICB5bGFiKCJOdW1iZXIgb2YgcGxvdHMiKQ0KYGBgDQoNCk51bWJlciBhbmQgcHJvcG9ydGlvbiBvZiBwbG90cyB3aXRoIG9ubHkgMSByZXN1cnZleSAoc2hvdWxkIG5vdCBiZSBzbyEpDQoNCmBgYHtyfQ0KbnJvdyhjb3VudF9yZXN1cnZleXMlPiVmaWx0ZXIoZGlzdGluY3RfeWVhcnM9PTEpKQ0KbnJvdyhjb3VudF9yZXN1cnZleXMlPiVmaWx0ZXIoZGlzdGluY3RfeWVhcnM9PTEpKS9ucm93KGNvdW50X3Jlc3VydmV5cykNCmBgYA0KDQpJbG9uYSBzZW50IGZpbGUgImlzc3VlNF9yZW1hcmtzLnhsc3giLg0KDQpCYXNlZCBvbiB0aGUgaW5mbyBpbiB0aGUgInJlbWFyayIgY29sdW1uIGluIHRoaXMgZmlsZSwgSSBjcmVhdGVkIGEgbmV3IGNvbHVtbiAiY29ycmVjdCIgKGluIGEgbmV3IGZpbGUsIGxvY2F0ZWQgaW4gdGhlIGRhdGEvZWRpdGVkIGZvbGRlcikuIFRoaXMgY29sdW1uIHNob3dzOg0KDQotIG1hbnVhbGx5OiB0aGVzZSByb3dzIHNob3VsZCBiZSBtYW51YWxseSBjb3JyZWN0ZWQgYmFzZWQgb24gdGhlIGluZm8gb24gdGhlICJyZW1hcmsiIGNvbHVtbg0KDQotIG5vdF9yZXN1cnY6IHRoZXNlIHJvd3MgZG8gbm90IGJlbG9uZyB0byByZXN1cnZleXMsIHNvIFJlU3VydmV5IHBsb3QgKFkvTikgc2hvdWxkIGJlIGNoYW5nZWQgdG8gTiwgYW5kIHRoZXNlIHJvd3Mgc2hvdWxkIGJlIGxhdGVyIGFkZGVkIHRvIGRiX0VWQQ0KDQotIHJlbW92ZTogdGhlc2Ugcm93cyBoYXZlIGRpZmZlcmVudCBkZXNpZ25zIGFuZCBzaG91bGQgYmUgZXhjbHVkZWQNCg0KLSB3YWl0OiBJbG9uYSBzZW50IGVtYWlsIHRvIGN1c3RvZGlhbiBhbmQgd2UgYXJlIHdhaXRpbmcgZm9yIHJlc3BvbnNlDQoNClJlYWQgdGhlIGZpbGUgaW4gdGhlIGVkaXRlZCBkYXRhIGZvbGRlci4NCg0KYGBge3J9DQppc3N1ZTRfcmVtYXJrcyA8LSByZWFkX2V4Y2VsKA0KICBoZXJlKCJkYXRhIiwgImVkaXRlZCIsImlzc3VlNF9yZW1hcmtzX2VkaXRlZEFWLnhsc3giKSkNCmBgYA0KDQojIyBub3RfcmVzdXJ2DQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X3VwZGF0ZWQgPC0gZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogICMgSm9pbiBpc3N1ZTRfcmVtYXJrcw0KICBsZWZ0X2pvaW4oDQogICAgaXNzdWU0X3JlbWFya3MgJT4lDQogICAgICBzZWxlY3QoUlNfQ09ERSwgYFJlU3VydmV5IHNpdGVgLCBgUmVTdXJ2ZXkgcGxvdGAsIGNvcnJlY3QpDQogICAgKSAlPiUNCiAgIyBDcmVhdGUgYSBjb2x1bW4gZWRpdF9ub3RfcmVzdXJ2IHRvIG1hcmsgcm93cyB0byB1cGRhdGUNCiAgbXV0YXRlKGVkaXRfbm90X3Jlc3VydiA9IGlmZWxzZShjb3JyZWN0ID09ICJub3RfcmVzdXJ2IiwgVFJVRSwgRkFMU0UpKSAlPiUNCiAgIyBTZXQgTkEgdmFsdWVzIGZvciBlZGl0X25vdF9yZXN1cnYgYXMgRkFMU0UNCiAgbXV0YXRlKGVkaXRfbm90X3Jlc3VydiA9IGlmZWxzZShpcy5uYShlZGl0X25vdF9yZXN1cnYpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZBTFNFLCBlZGl0X25vdF9yZXN1cnYpKQ0KYGBgDQoNClNhdmUgcm93cyB0byBhZGQgbGF0ZXIgdG8gRVZBLWRiLg0KDQpgYGB7cn0NCndyaXRlX3RzdihkYl9yZXN1cnZfdXBkYXRlZCAlPiUgZmlsdGVyKGVkaXRfbm90X3Jlc3VydiA9PSBUKSwNCiAgICAgICAgICBoZXJlKCJkYXRhIiwgImNsZWFuIiwiZGJfYWRkX3RvX0VWQS5jc3YiKSkNCmBgYA0KDQpSZW1vdmUgdGhvc2Ugcm93cyBmcm9tIGRiX3Jlc3Vydl91cGRhdGVkIGFuZCByZW1vdmUgY29sdW1uICJlZGl0X25vdF9yZXN1cnYiLg0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkIDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICBmaWx0ZXIoZWRpdF9ub3RfcmVzdXJ2ID09IEYpICU+JSBzZWxlY3QoLWVkaXRfbm90X3Jlc3VydikNCmBgYA0KDQojIyByZW1vdmUNCg0KUmVtb3ZlIHJvd3Mgd2hlcmUgY29sdW1uICJjb3JyZWN0IiBpcyBlcXVhbCB0byAicmVtb3ZlIi4NCg0KYGBge3J9DQpkYl9yZXN1cnZfdXBkYXRlZCA8LSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgZmlsdGVyKGNvcnJlY3QgIT0gInJlbW92ZSIgfCBpcy5uYShjb3JyZWN0KSkNCmBgYA0KDQojIyBtYW51YWxseQ0KDQpgYGB7cn0NCm5yb3dzX2NvcnJfbWFudWFsbHkgPC0gbnJvdyhkYl9yZXN1cnZfdXBkYXRlZCAlPiUgZmlsdGVyKGNvcnJlY3QgPT0gIm1hbnVhbGx5IikpDQpgYGANCg0KSSBuZWVkIHRvIGNvcnJlY3QgYG5yb3dzX2NvcnJfbWFudWFsbHlgIHJvd3MgbWFudWFsbHkuDQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lIGZpbHRlcihjb3JyZWN0ID09ICJtYW51YWxseSIpICU+JSBjb3VudChSU19DT0RFKQ0KYGBgDQoNCg0KIyMjIEFUXzAwMDENCg0KYGBge3J9DQpjb3VudF9yZXN1cnZleXMgJT4lIGZpbHRlcihSU19DT0RFID09ICJBVF8wMDAxIiAmIGRpc3RpbmN0X3llYXJzID09IDEpDQpgYGANCg0KUmVtYXJrIGluIElsb25hJ3MgZmlsZTogd3JvbmcgeWVhciBmb3IgMSBvYnNlcnZhdGlvbiwgc2VlIHJzX3RpbWUgKHdoZW4gdmFsdWUgMSwgeW9lciBzaG91bGQgYmUgMjAwMykgLSB0eXBpbmcgbWlzdGFrZS4NCg0KVXBkYXRlIGRiX3Jlc3Vydl91cGRhdGVkLg0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkIDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAjIENyZWF0ZSBhIGNvbHVtbiBlZGl0X2RhdGUgdG8gbWFyayByb3dzIHRvIHVwZGF0ZQ0KICBtdXRhdGUoZWRpdF9kYXRlID0gUlNfQ09ERSA9PSAiQVRfMDAwMSIgJg0KICAgICAgICAgICAoYFJlU3VydmV5IHBsb3RgID09ICIxNyIgfCBgUmVTdXJ2ZXkgcGxvdGAgPT0gIkFfMjk1NSIpICYNCiAgICAgICAgICAgUlNfVElNRSA9PSAxKSAlPiUNCiAgIyBVcGRhdGUgYERhdGUgb2YgcmVjb3JkaW5nYCBpZiBlZGl0X2RhdGUgPSBUUlVFDQogIG11dGF0ZSgNCiAgICBgRGF0ZSBvZiByZWNvcmRpbmdgID0gaWZfZWxzZSgNCiAgICAgIGVkaXRfZGF0ZSwNCiAgICAgIHN0cl9yZXBsYWNlX2FsbChgRGF0ZSBvZiByZWNvcmRpbmdgLCAiMjAyMiIsICIyMDAzIiksDQogICAgICBgRGF0ZSBvZiByZWNvcmRpbmdgKQ0KICAgICkNCmBgYA0KDQojIyMgQ1pfMDAwMQ0KDQpSZW1hcmsgaW4gSWxvbmEncyBmaWxlOiBSZXBlYXRlZCBSU19wbG90IHNob3VsZCBiZSBTbGFuYTEuDQoNCkNoYW5nZSBSZVN1cnZleSBzaXRlIGZyb20gIlNsYW5hMSIgdG8gIlNsYW5hIi4NCg0KVXBkYXRlIGRiX3Jlc3Vydl91cGRhdGVkLg0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkIDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAjIENyZWF0ZSBhIGNvbHVtbiBlZGl0X2RhdGUgdG8gbWFyayByb3dzIHRvIHVwZGF0ZQ0KICBtdXRhdGUoZWRpdF9zaXRlID0gYFJlU3VydmV5IHNpdGVgID09ICJTbGFuYTEiKSAlPiUNCiAgIyBVcGRhdGUgYFJlU3VydmV5IHNpdGVgIGlmIGVkaXRfc2l0ZSA9IFRSVUUNCiAgbXV0YXRlKA0KICAgIGBSZVN1cnZleSBzaXRlYCA9IGlmX2Vsc2UoDQogICAgICBlZGl0X3NpdGUsIA0KICAgICAgc3RyX3JlcGxhY2VfYWxsKGBSZVN1cnZleSBzaXRlYCwgIlNsYW5hMSIsICJTbGFuYSIpLA0KICAgICAgYFJlU3VydmV5IHNpdGVgKQ0KICAgICkNCmBgYA0KDQojIyMgRVNfMDAwMWQNCg0KUmVtYXJrIGluIElsb25hJ3MgZmlsZTogaXMgcmVwZWF0ZWQsIGNvcnJlY3RlZCBkYXRlIChJIHJlbWVtYmVyIHNvbWV0aW1lcyBsYXN0IHllYXIpLg0KDQpDb3JyZWN0IGRhdGUgb2Ygc2Vjb25kIHJlc3VydmV5IChSU19USU1FID09IDIpIHRvIDIwMTguDQoNClVwZGF0ZSBkYl9yZXN1cnZfdXBkYXRlZC4NCg0KYGBge3J9DQpkYl9yZXN1cnZfdXBkYXRlZCA8LSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgIyBBcHByb2FjaCBjcmVhdGluZyBhbiBVcGRhdGVGbGFnIGFzc2lnbnMgTkFzIHRvIHNldmVyYWwgZGF0ZXMsIG5vdCBzdXJlIHdoeSENCiAgIyBVcGRhdGUgYERhdGUgb2YgcmVjb3JkaW5nYCBqdXN0IGZvciB0aGUgcGFydGljdWxhciBQbG90T2JzZXJ2YXRpb25JRA0KICBtdXRhdGUoDQogICAgYERhdGUgb2YgcmVjb3JkaW5nYCA9IGlmX2Vsc2UoDQogICAgICBQbG90T2JzZXJ2YXRpb25JRCA9PSAiNTIzNjE1IiwNCiAgICAgIHN0cl9yZXBsYWNlX2FsbChgRGF0ZSBvZiByZWNvcmRpbmdgLCAiMjAwNSIsICIyMDE4IiksDQogICAgICBgRGF0ZSBvZiByZWNvcmRpbmdgKQ0KICAgICkgJT4lDQogICMgVXBkYXRlIGNvbHVtbiBlZGl0X2RhdGUgdG8gbGFiZWwgZWRpdHMNCiAgbXV0YXRlKGVkaXRfZGF0ZSA9IGlmX2Vsc2UoUGxvdE9ic2VydmF0aW9uSUQgPT0gIjUyMzYxNSIsIFRSVUUsIGVkaXRfZGF0ZSkpDQpgYGANCg0KIyMjIEZSXzAwMDJmDQoNClJlbWFyayBpbiBJbG9uYSdzIGZpbGU6IGNvcnJlY3RlZCBuYW1lIG9mIHRoaXMgcGxvdC4NCg0KQ2hhbmdlIFJlU3VydmV5IHBsb3QgZnJvbSAiUFNFVDMtNCIgdG8gIlBTRVQgMy00Ig0KDQpVcGRhdGUgZGJfcmVzdXJ2X3VwZGF0ZWQuDQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X3VwZGF0ZWQgPC0gZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogICMgQ3JlYXRlIGFuIFVwZGF0ZUZsYWcgdG8gbWFyayByb3dzIHRvIHVwZGF0ZQ0KICBtdXRhdGUoVXBkYXRlRmxhZyA9IGBSZVN1cnZleSBwbG90YCA9PSAiUFNFVDMtNCIpICU+JQ0KICAjIFVwZGF0ZSBgUmVTdXJ2ZXkgcGxvdGAgaWYgVXBkYXRlRmxhZyA9IFRSVUUNCiAgbXV0YXRlKA0KICAgIGBSZVN1cnZleSBwbG90YCA9IGlmX2Vsc2UoVXBkYXRlRmxhZywgIlBTRVQgMy00IiwgYFJlU3VydmV5IHBsb3RgKQ0KICAgICkgJT4lDQogICMgVXBkYXRlIGNvbHVtbiBlZGl0X3Bsb3QgdG8gbGFiZWwgZWRpdHMNCiAgbXV0YXRlKGVkaXRfcGxvdCA9IGlmX2Vsc2UoVXBkYXRlRmxhZywgVFJVRSwgZWRpdF9wbG90KSkgJT4lDQogICMgUmVtb3ZlIHVubmVlZGVkIGNvbHVtbg0KICBzZWxlY3QoLVVwZGF0ZUZsYWcpDQpgYGANCg0KIyMjIEZSXzAwMDJoDQoNClJlbWFyayBpbiBJbG9uYSdzIGZpbGU6IGNvcnJlY3RlIGRuYW1lIGZvciBQU0VUIFQyIDUtNiwgcmVwZWF0ZWQgM3gNCg0KQ2hhbmdlIFJlU3VydmV5IHBsb3QgZnJvbSAiUFNFVCBUMiAgNS02IiB0byAiUFNFVCBUMiA1LTYiLg0KDQpVcGRhdGUgZGJfcmVzdXJ2X3VwZGF0ZWQuDQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X3VwZGF0ZWQgPC0gZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogICMgQ3JlYXRlIGFuIFVwZGF0ZUZsYWcgdG8gbWFyayByb3dzIHRvIHVwZGF0ZQ0KICBtdXRhdGUoVXBkYXRlRmxhZyA9IGBSZVN1cnZleSBwbG90YCA9PSAiUFNFVCBUMiAgNS02IikgJT4lDQogICMgVXBkYXRlIGBSZVN1cnZleSBwbG90YCBpZiBVcGRhdGVGbGFnID0gVFJVRQ0KICBtdXRhdGUoDQogICAgYFJlU3VydmV5IHBsb3RgID0gaWZfZWxzZShVcGRhdGVGbGFnLCAiUFNFVCBUMiA1LTYiLCBgUmVTdXJ2ZXkgcGxvdGApDQogICAgKSAlPiUNCiAgIyBVcGRhdGUgY29sdW1uIGVkaXRfcGxvdCB0byBsYWJlbCBlZGl0cw0KICBtdXRhdGUoZWRpdF9wbG90ID0gaWZfZWxzZShVcGRhdGVGbGFnLCBUUlVFLCBlZGl0X3Bsb3QpKSAlPiUNCiAgIyBSZW1vdmUgdW5uZWVkZWQgY29sdW1uDQogIHNlbGVjdCgtVXBkYXRlRmxhZykNCmBgYA0KDQojIyMgSVRfMDAwMWMNCg0KUmVtYXJrIGluIElsb25hJ3MgZmlsZTogY2hubmdlIG9mIGNvZGluZyBSU19wbG90LCBzb21lIHBsb3RzIGhhdmUgaW5kZXggYSAoc2FtZSBhcmVhIGFzIG9sZCBvYnNlcnYpIG9yIGIgKHNtYWxsZXIgQXJlYSksIGluZGV4ZXMga2VwdCBpbiByc19vYnNlcnYuDQoNClJlbW92ZSAiYiIgYW5kICJhIiBmcm9tIFJlU3VydmV5IHBsb3QuDQoNClVwZGF0ZSBkYl9yZXN1cnZfdXBkYXRlZC4NCg0KYGBge3J9DQpkYl9yZXN1cnZfdXBkYXRlZCA8LSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgIyBDcmVhdGUgYW4gVXBkYXRlRmxhZyB0byBtYXJrIHJvd3MgdG8gdXBkYXRlDQogIG11dGF0ZSgNCiAgICBVcGRhdGVGbGFnID0gUlNfQ09ERSA9PSAiSVRfMDAwMWMiICYgY29ycmVjdCA9PSAibWFudWFsbHkiICYNCiAgICAgIChzdHJfZGV0ZWN0KGBSZVN1cnZleSBwbG90YCwgImEiKSB8IHN0cl9kZXRlY3QoYFJlU3VydmV5IHBsb3RgLCAiYiIpKQ0KICAgICkgJT4lDQogICMgVXBkYXRlIGBSZVN1cnZleSBwbG90YCBpZiBVcGRhdGVGbGFnID0gVFJVRQ0KICBtdXRhdGUoDQogICAgYFJlU3VydmV5IHBsb3RgID0gaWZfZWxzZSgNCiAgICAgIFVwZGF0ZUZsYWcsDQogICAgICBzdHJfcmVwbGFjZV9hbGwoYFJlU3VydmV5IHBsb3RgLCAiW2FiXSIsICIiKSwgICMgTWF0Y2hlcyBib3RoICJhIiBhbmQgImIiDQogICAgICBgUmVTdXJ2ZXkgcGxvdGApDQogICAgKSAlPiUNCiAgIyBVcGRhdGUgY29sdW1uIGVkaXRfcGxvdCB0byBsYWJlbCBlZGl0cw0KICBtdXRhdGUoZWRpdF9wbG90ID0gaWZfZWxzZShVcGRhdGVGbGFnLCBUUlVFLCBlZGl0X3Bsb3QpKSAlPiUNCiAgIyBSZW1vdmUgdW5uZWVkZWQgY29sdW1uDQogIHNlbGVjdCgtVXBkYXRlRmxhZykNCmBgYA0KDQpBbHNvIGNoYW5nZSBSZVN1cnZleSBwbG90IDg5NCB0byA4OSB3aGljaCBzZWVtcyB0byBiZSB0aGUgY29ycmVjdCBudW1iZXIuDQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X3VwZGF0ZWQgPC0gZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogICMgQ3JlYXRlIGFuIFVwZGF0ZUZsYWcgdG8gbWFyayByb3dzIHRvIHVwZGF0ZQ0KICBtdXRhdGUoDQogICAgVXBkYXRlRmxhZyA9IFJTX0NPREUgPT0gIklUXzAwMDFjIiAmIGNvcnJlY3QgPT0gIm1hbnVhbGx5IiAmDQogICAgICBgUmVTdXJ2ZXkgcGxvdGAgPT0gIjg5NCINCiAgICApICU+JQ0KICAjIFVwZGF0ZSBgUmVTdXJ2ZXkgcGxvdGAgaWYgVXBkYXRlRmxhZyA9IFRSVUUNCiAgbXV0YXRlKGBSZVN1cnZleSBwbG90YCA9IGlmX2Vsc2UoVXBkYXRlRmxhZywgIjg5IiwgYFJlU3VydmV5IHBsb3RgKSkgJT4lDQogICMgVXBkYXRlIGNvbHVtbiBlZGl0X3Bsb3QgdG8gbGFiZWwgZWRpdHMNCiAgbXV0YXRlKGVkaXRfcGxvdCA9IGlmX2Vsc2UoVXBkYXRlRmxhZywgVFJVRSwgZWRpdF9wbG90KSkgJT4lDQogICMgUmVtb3ZlIHVubmVlZGVkIGNvbHVtbg0KICBzZWxlY3QoLVVwZGF0ZUZsYWcpDQpgYGANCg0KIyMjIElUXzAwMDFkDQoNClJlbWFyayBpbiBJbG9uYSdzIGZpbGU6IDEgb2xkIGNvcnJlc3BvbmQgdG8gbW9yZSBuZXcgb2JzZXJ2YXRpb24gQSxCLEMgLSByZW1vdmVkIGluZGV4ZXMgZnJvbSBwbG90cywga2VwdCBpbiBvYnNlcnZhdGlvbnMuDQoNClJlbW92ZSAiQSIsICJCIiBhbmQgIkMiIGZyb20gUmVTdXJ2ZXkgcGxvdC4NCg0KVXBkYXRlIGRiX3Jlc3Vydl91cGRhdGVkLg0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkIDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAjIENyZWF0ZSBhbiBVcGRhdGVGbGFnIHRvIG1hcmsgcm93cyB0byB1cGRhdGUNCiAgbXV0YXRlKA0KICAgIFVwZGF0ZUZsYWcgPSBSU19DT0RFID09ICJJVF8wMDAxZCIgJiBjb3JyZWN0ID09ICJtYW51YWxseSIgJg0KICAgICAgKHN0cl9kZXRlY3QoYFJlU3VydmV5IHBsb3RgLCAiQSIpIHwgc3RyX2RldGVjdChgUmVTdXJ2ZXkgcGxvdGAsICJCIikgfA0KICAgICAgICAgc3RyX2RldGVjdChgUmVTdXJ2ZXkgcGxvdGAsICJDIikpDQogICAgKSAlPiUNCiAgIyBVcGRhdGUgYFJlU3VydmV5IHBsb3RgIGlmIFVwZGF0ZUZsYWcgPSBUUlVFDQogIG11dGF0ZSgNCiAgICBgUmVTdXJ2ZXkgcGxvdGAgPSBpZl9lbHNlKA0KICAgICAgVXBkYXRlRmxhZywNCiAgICAgIHN0cl9yZXBsYWNlX2FsbChgUmVTdXJ2ZXkgcGxvdGAsICJbQUJDXSIsICIiKSwNCiAgICAgIGBSZVN1cnZleSBwbG90YCkNCiAgICApICU+JQ0KICAjIFVwZGF0ZSBjb2x1bW4gZWRpdF9wbG90IHRvIGxhYmVsIGVkaXRzDQogIG11dGF0ZShlZGl0X3Bsb3QgPSBpZl9lbHNlKFVwZGF0ZUZsYWcsIFRSVUUsIGVkaXRfcGxvdCkpICU+JQ0KICAjIFJlbW92ZSB1bm5lZWRlZCBjb2x1bW4NCiAgc2VsZWN0KC1VcGRhdGVGbGFnKQ0KYGBgDQoNCiMjIyBJVF8wMDAxZQ0KDQpSZW1hcmsgaW4gSWxvbmEncyBmaWxlOiBjaGFuZ2Ugb2YgUlNfUExPVFMgKDEgb3JpZ2luYWwgMU8sIG1vcmUgbmV3IDFzID0gc3RhbmRhcmQgc2l6ZSAxMDBtLCBhLCBiLCBjID0gc2FtZSwgbGFyZ2VyIHNpemUpLg0KDQpSZW1vdmUgImEiLCAiYiIsICJjIiBhbmQgInMiIGZyb20gUmVTdXJ2ZXkgcGxvdC4NCg0KVXBkYXRlIGRiX3Jlc3Vydl91cGRhdGVkLg0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkIDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAjIENyZWF0ZSBhbiBVcGRhdGVGbGFnIHRvIG1hcmsgcm93cyB0byB1cGRhdGUNCiAgbXV0YXRlKA0KICAgIFVwZGF0ZUZsYWcgPSBSU19DT0RFID09ICJJVF8wMDAxZSIgJiBjb3JyZWN0ID09ICJtYW51YWxseSIgJg0KICAgICAgKHN0cl9kZXRlY3QoYFJlU3VydmV5IHBsb3RgLCAiYSIpIHwgc3RyX2RldGVjdChgUmVTdXJ2ZXkgcGxvdGAsICJiIikgfA0KICAgICAgICAgc3RyX2RldGVjdChgUmVTdXJ2ZXkgcGxvdGAsICJjIikgfCBzdHJfZGV0ZWN0KGBSZVN1cnZleSBwbG90YCwgInMiKSkNCiAgICApICU+JQ0KICAjIFVwZGF0ZSBgUmVTdXJ2ZXkgcGxvdGAgaWYgVXBkYXRlRmxhZyA9IFRSVUUNCiAgbXV0YXRlKA0KICAgIGBSZVN1cnZleSBwbG90YCA9IGlmX2Vsc2UoDQogICAgICBVcGRhdGVGbGFnLA0KICAgICAgc3RyX3JlcGxhY2VfYWxsKGBSZVN1cnZleSBwbG90YCwgIlthYmNzXSIsICIiKSwNCiAgICAgIGBSZVN1cnZleSBwbG90YCkNCiAgICApICU+JQ0KICAjIFVwZGF0ZSBjb2x1bW4gZWRpdF9wbG90IHRvIGxhYmVsIGVkaXRzDQogIG11dGF0ZShlZGl0X3Bsb3QgPSBpZl9lbHNlKFVwZGF0ZUZsYWcsIFRSVUUsIGVkaXRfcGxvdCkpICU+JQ0KICAjIFJlbW92ZSB1bm5lZWRlZCBjb2x1bW4NCiAgc2VsZWN0KC1VcGRhdGVGbGFnKQ0KYGBgDQoNCiMjIyBMVl8wMDAxYg0KDQpSZW1hcmsgaW4gSWxvbmEncyBmaWxlOiBjaGFuZ2VkIGZvciBCMTAsICEhIEZvciB3aG9sZSBkYXRhc2V0IEkgY2hhbmdlZCBSU19jb2RlcyAtIGFkZGVkIHBsb3Qgc2l6ZXMgLSB0byBiZSBhYmxlIHRvIGxpbmsgc2FtZSBzaXplIHRocm91Z2ggeWVhcnMsIG5lc3RlZCBkZXNpZ24uDQoNCkNoYW5nZSBSZVN1cnZleSBwbG90IGIxMCB0byBCMTAuDQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X3VwZGF0ZWQgPC0gZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogICMgQ3JlYXRlIGFuIFVwZGF0ZUZsYWcgdG8gbWFyayByb3dzIHRvIHVwZGF0ZQ0KICBtdXRhdGUoDQogICAgVXBkYXRlRmxhZyA9IFJTX0NPREUgPT0gIkxWXzAwMDFiIiAmIGNvcnJlY3QgPT0gIm1hbnVhbGx5IiAmDQogICAgICBgUmVTdXJ2ZXkgcGxvdGAgPT0gImIxMCINCiAgICApICU+JQ0KICAjIFVwZGF0ZSBgUmVTdXJ2ZXkgcGxvdGAgaWYgVXBkYXRlRmxhZyA9IFRSVUUNCiAgbXV0YXRlKGBSZVN1cnZleSBwbG90YCA9IGlmX2Vsc2UoVXBkYXRlRmxhZywgIkIxMCIsIGBSZVN1cnZleSBwbG90YCkpICU+JQ0KICAjIFVwZGF0ZSBjb2x1bW4gZWRpdF9wbG90IHRvIGxhYmVsIGVkaXRzDQogIG11dGF0ZShlZGl0X3Bsb3QgPSBpZl9lbHNlKFVwZGF0ZUZsYWcsIFRSVUUsIGVkaXRfcGxvdCkpICU+JQ0KICAjIFJlbW92ZSB1bm5lZWRlZCBjb2x1bW4NCiAgc2VsZWN0KC1VcGRhdGVGbGFnKQ0KYGBgDQoNCkFsbCByb3dzIGhhdmUgUmVsZXbDqSBhcmVhIChtwrIpLiBMYXRlciB3ZSBzaG91bGQgc2VsZWN0IHJvd3Mgd2l0aCB0aGUgc2FtZSBSZWxldsOpIGFyZWEgKG3CsikgdGhyb3VnaG91dCB0aGUgeWVhcnMsIGFuZCBkaXNjYXJkIG90aGVycy4NCg0KRm9yIGNhc2VzIHdoZXJlIHRoZXJlIGFyZSBtdWx0aXBsZSBvYnNlcnZhdGlvbnMgcGVyIHllYXIsIEkgY2FuIHVzZSBSZWxldsOpIGFyZWEgKG3CsikgdG8gc2VsZWN0IG9uZSBvZiB0aGUgb2JzZXJ2YXRpb25zICh0aGUgb25lIHRoYXQgaGFzIGFuIGFyZWEgZXF1YWwgdG8gdGhlIGFyZWEgaW4gb3RoZXIgeWVhcnMpLiANCg0KIyMjIE5PXzAwMDENCg0KUmVtYXJrIGluIElsb25hJ3MgZmlsZTogcmVzYW1wbGluZyBOLU4sIGJvdGggb2xkIGFuZCBuZXcgZnJvbSBzYW1lIGxvY2FsaXR5LCByc19wbG90IGNoYW5nZWQgZm9yIFVsbGVyZW5nc2FuZGVuX1VsbGVyZW5nc2xhZ3VuYSwgcHJlY2lzaW9uIG9mciBvbGQgc2V0IGZvciAxMDAwIG0uDQoNCkNoYW5nZSBSZVN1cnZleSBwbG90IFVsbGVyZW5nc2FuZGVuIGFuZCBVbGxlcmVuZ3NsYWd1bmEgdG8gVWxsZXJlbmdzYW5kZW5fVWxsZXJlbmdzbGFndW5hLg0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkIDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAjIENyZWF0ZSBhbiBVcGRhdGVGbGFnIHRvIG1hcmsgcm93cyB0byB1cGRhdGUNCiAgbXV0YXRlKA0KICAgIFVwZGF0ZUZsYWcgPSBSU19DT0RFID09ICJOT18wMDAxIiAmIGNvcnJlY3QgPT0gIm1hbnVhbGx5IiAmDQogICAgICAoYFJlU3VydmV5IHBsb3RgID09ICJVbGxlcmVuZ3NhbmRlbiIgfCANCiAgICAgICAgIGBSZVN1cnZleSBwbG90YCA9PSAiVWxsZXJlbmdzbGFndW5hIikNCiAgICApICU+JQ0KICAjIFVwZGF0ZSBgUmVTdXJ2ZXkgcGxvdGAgaWYgVXBkYXRlRmxhZyA9IFRSVUUNCiAgbXV0YXRlKGBSZVN1cnZleSBwbG90YCA9IGlmX2Vsc2UoVXBkYXRlRmxhZywgIlVsbGVyZW5nc2FuZGVuX1VsbGVyZW5nc2xhZ3VuYSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBSZVN1cnZleSBwbG90YCkpICU+JQ0KICAjIFVwZGF0ZSBjb2x1bW4gZWRpdF9wbG90IHRvIGxhYmVsIGVkaXRzDQogIG11dGF0ZShlZGl0X3Bsb3QgPSBpZl9lbHNlKFVwZGF0ZUZsYWcsIFRSVUUsIGVkaXRfcGxvdCkpICU+JQ0KICAjIENoYW5nZSBMb2NhdGlvbiB1bmNlcnRhaW50eSAobSkgdG8gMTAwMCBtIHdoZW4gUlNfVElNRSA9PSAxDQogICMgQ3JlYXRlIGEgY29sdW1uIGVkaXRfdW5jIHRvIG1hcmsgcm93cyB0byB1cGRhdGUNCiAgbXV0YXRlKGVkaXRfdW5jID0gVXBkYXRlRmxhZyAmIFJTX1RJTUUgPT0gMSkgJT4lDQogICMgVXBkYXRlIGBMb2NhdGlvbiB1bmNlcnRhaW50eSAobSlgIGlmIGVkaXRfdW5jID0gVFJVRQ0KICBtdXRhdGUoYExvY2F0aW9uIHVuY2VydGFpbnR5IChtKWAgPSBpZmVsc2UoZWRpdF91bmMsIDEwMDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgTG9jYXRpb24gdW5jZXJ0YWludHkgKG0pYCkpICU+JQ0KIyBSZW1vdmUgdW5uZWVkZWQgY29sdW1uDQogIHNlbGVjdCgtVXBkYXRlRmxhZykNCmBgYA0KDQojIyMgU0lfMDAwMmENCg0KUmVtYXJrIGluIElsb25hJ3MgZmlsZTogZXJyb3IgaW4gUlNfU0lURSBjaGFuZ2VkIGZvciBSRi4NCg0KQ2hhbmdlIFJlU3VydmV5IHNpdGUgRlMgdG8gUlMuDQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X3VwZGF0ZWQgPC0gZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogICMgQ3JlYXRlIGFuIFVwZGF0ZUZsYWcgdG8gbWFyayByb3dzIHRvIHVwZGF0ZQ0KICBtdXRhdGUoDQogICAgVXBkYXRlRmxhZyA9IFJTX0NPREUgPT0gIlNJXzAwMDJhIiAmIGNvcnJlY3QgPT0gIm1hbnVhbGx5IiAmDQogICAgICBgUmVTdXJ2ZXkgc2l0ZWAgPT0gIkZTIg0KICAgICkgJT4lDQogICMgVXBkYXRlIGBSZVN1cnZleSBzaXRlYCBpZiBVcGRhdGVGbGFnID0gVFJVRQ0KICBtdXRhdGUoYFJlU3VydmV5IHNpdGVgID0gaWZfZWxzZShVcGRhdGVGbGFnLCAiUlMiLCBgUmVTdXJ2ZXkgc2l0ZWApKSAlPiUNCiAgIyBVcGRhdGUgY29sdW1uIGVkaXRfc2l0ZSB0byBsYWJlbCBlZGl0cw0KICBtdXRhdGUoZWRpdF9zaXRlID0gaWZfZWxzZShVcGRhdGVGbGFnLCBUUlVFLCBlZGl0X3NpdGUpKSAlPiUNCiAgIyBSZW1vdmUgdW5uZWVkZWQgY29sdW1uDQogIHNlbGVjdCgtVXBkYXRlRmxhZykNCmBgYA0KDQojIyMgVUFfMDAwMQ0KDQpUZXh0IGluIElsb25hJ3MgZW1haWw6IE9uZSBjb3JyZWN0aW9uIGZvciBpc3N1ZTQg4oCTIDYgY29vcmRpbmF0ZXMgYXJlIGNvcnJlY3RlZCDigJMgaG93ZXZlciB0aGlzIERCIOKAkyBoYXMgYW5vdGhlciBwcm9ibGVtIHdpdGggcnNfY29kZXMgKHNlZW1zIHRvIG1lIHRoYXQgb3JpZ2luYWwgY29kaW5nIG9mIHBsb3RzIGFyZSB3cm9uZyDigJMgdGhhdMK0cyB3aHkgeW91IGhhdmUgbm90IHJlcGVhdGluZyBwbG90cykgYW5kIGFsc28gb25lIGdyb3VwIG9mIG9ic2VydmF0aW9uIGhhcyBhbHNvIGR1YmlvdXMgY29vcmRpbmF0ZXMuIEkgYW0gd2FpdGluZyBmb3IgYW5vdGhlciBjb3JyZWN0aW9ucy4NCg0KYGBge3J9DQppc3N1ZTRfVUFfMDAwMSA8LSByZWFkX2V4Y2VsKA0KICBoZXJlKCJkYXRhIiwgInJhdyIsICJEYXRhX2NvcnJlY3Rpb25zX0lsb25hIiwgIklzc3VlIDQiLA0KICAgICAgICJVQV8wMDAxX2lzc3VlNF9vbmx5Y29ycmVjdGVkLnhsc3giKSkNCmBgYA0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkIDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAjIENyZWF0ZSBhbiBVcGRhdGVGbGFnIHRvIG1hcmsgcm93cyB0byB1cGRhdGUNCiAgbXV0YXRlKFVwZGF0ZUZsYWcgPSBQbG90T2JzZXJ2YXRpb25JRCAlaW4lIA0KICAgICAgICAgICBjKDM4MjI5OSwgMzgyMzAxLCAzODIzMjUsIDM4MjMxNCwgMzgyMzIyLCAzODIzMjQpKSAlPiUNCiAgIyBKb2luIHdpdGggaXNzdWU0X1VBXzAwMDEgYmFzZWQgb24gUGxvdE9ic2VydmF0aW9uSUQNCiAgbGVmdF9qb2luKGlzc3VlNF9VQV8wMDAxICU+JQ0KICAgICAgICAgICAgICBtdXRhdGUoUGxvdE9ic2VydmF0aW9uSUQgPSBQbG90SUQpICU+JQ0KICAgICAgICAgICAgICBzZWxlY3QoUGxvdE9ic2VydmF0aW9uSUQsIExPTkdJVFVERSwgTEFUSVRVREUpKSAlPiUNCiAgIyBVcGRhdGUgTG9uZ2l0dWRlIGFuZCBMYXRpdHVkZSB3aXRoIHRoZSBuZXcgdmFsdWVzDQogICMgaWYgVXBkYXRlRmxhZyA9IFRSVUUNCiAgbXV0YXRlKA0KICAgIExvbmdpdHVkZSA9IGlmX2Vsc2UoVXBkYXRlRmxhZywgTE9OR0lUVURFLCBMb25naXR1ZGUpLA0KICAgIExhdGl0dWRlID0gaWZfZWxzZShVcGRhdGVGbGFnLCBMQVRJVFVERSwgTGF0aXR1ZGUpDQogICAgKSAlPiUNCiAgIyBVcGRhdGUgUmVTdXJ2ZXkgc2l0ZSB3aXRoICJTeXZ1bGthXzUiIGlmIFVwZGF0ZUZsYWcgPSBUUlVFDQogIG11dGF0ZShgUmVTdXJ2ZXkgc2l0ZWAgPSBpZl9lbHNlKFVwZGF0ZUZsYWcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTeXZ1bGthXzUiLCBgUmVTdXJ2ZXkgc2l0ZWApKSAlPiUNCiAgIyBVcGRhdGUgY29sdW1uIGVkaXRfY29vcmRzIHRvIGxhYmVsIGVkaXRzDQogIG11dGF0ZShlZGl0X2Nvb3JkcyA9IGlmX2Vsc2UoVXBkYXRlRmxhZywgVFJVRSwgZWRpdF9jb29yZHMpKSAlPiUNCiAgIyBVcGRhdGUgY29sdW1uIGVkaXRfc2l0ZSB0byBsYWJlbCBlZGl0cw0KICBtdXRhdGUoZWRpdF9zaXRlID0gaWZfZWxzZShVcGRhdGVGbGFnLCBUUlVFLCBlZGl0X3NpdGUpKSAlPiUNCiAgIyBSZW1vdmUgdW5uZWVkZWQgY29sdW1ucw0KICBzZWxlY3QoLVVwZGF0ZUZsYWcsIC1MT05HSVRVREUsIC1MQVRJVFVERSkNCmBgYA0KDQojIyBSZW1vdmUgdW5uZWVkZWQgY29sdW1uDQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X3VwZGF0ZWQgPC0gZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogICMgUmVtb3ZlICJjb3JyZWN0IiBjb2x1bW4gKG5vdCBuZWVkZWQgYW55bW9yZSkNCiAgc2VsZWN0KC1jb3JyZWN0KQ0KYGBgDQoNCiMjIFJlY2FsY3VsYXRlIGNvdW50IHJlc3VydmV5cw0KDQpgYGB7cn0NCmNvdW50X3Jlc3VydmV5cyA8LSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgIyBDb252ZXJ0IGRhdGVzIHRvIGRhdGUgZm9ybWF0IGFuZCBnZXQgdGhlIHllYXINCiAgbXV0YXRlKGRhdGUgPSBkbXkoYERhdGUgb2YgcmVjb3JkaW5nYCksIHllYXIgPSB5ZWFyKGRhdGUpKSAlPiUNCiAjIEdyb3VwIGJ5IFJTX0NPREUsIGBSZVN1cnZleSBzaXRlYCwgYFJlU3VydmV5IHBsb3RgDQogIGdyb3VwX2J5KFJTX0NPREUsIGBSZVN1cnZleSBzaXRlYCwgYFJlU3VydmV5IHBsb3RgKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgICMgR2V0IGhvdyBtYW55IGRpZmZlcmVudCB5ZWFycyBmb3IgZWFjaCB1bmlxdWUgZ3JvdXANCiAgICBkaXN0aW5jdF95ZWFycz1uX2Rpc3RpbmN0KHllYXIpLCANCiAgICAjIEdldCBob3cgbWFueSBkaWZmZXJlbnQgZGF0ZXMgZm9yIGVhY2ggdW5pcXVlIGdyb3VwDQogICAgZGlzdGluY3RfZGF0ZXM9bl9kaXN0aW5jdChkYXRlKSwgLmdyb3VwcyA9ICJkcm9wIikNCmBgYA0KDQpTdW1tYXJ5IHN0YXRzOg0KDQpgYGB7cn0NCnN1bW1hcnkoY291bnRfcmVzdXJ2ZXlzJGRpc3RpbmN0X3llYXJzKQ0Kc2QoY291bnRfcmVzdXJ2ZXlzJGRpc3RpbmN0X3llYXJzKQ0KYGBgDQoNCkhpc3RvZ3JhbXM6DQoNCmBgYHtyfQ0KIyBGb3IgYWxsIGRhdGENCmdncGxvdChjb3VudF9yZXN1cnZleXMsIGFlcyh4ID0gZGlzdGluY3RfeWVhcnMpKSArIA0KICBnZW9tX2hpc3RvZ3JhbShmaWxsID0gIndoaXRlIiwgY29sb3IgPSAiYmxhY2siLCBiaW5zID0gNTUpKw0KICB4bGFiKCJOdW1iZXIgb2YgUmVTdXJ2ZXkgb2JzZXJ2YXRpb25zIChkaWZmZXJlbnQgeWVhcnMpIikgKw0KICB5bGFiKCJOdW1iZXIgb2YgcGxvdHMiKQ0KYGBgDQoNCk51bWJlciBhbmQgcHJvcG9ydGlvbiBvZiBwbG90cyB3aXRoIG9ubHkgMSByZXN1cnZleSAoc2hvdWxkIG5vdCBiZSBzbyEpDQoNCmBgYHtyfQ0KbnJvdyhjb3VudF9yZXN1cnZleXMlPiVmaWx0ZXIoZGlzdGluY3RfeWVhcnM9PTEpKQ0KbnJvdyhjb3VudF9yZXN1cnZleXMlPiVmaWx0ZXIoZGlzdGluY3RfeWVhcnM9PTEpKS9ucm93KGNvdW50X3Jlc3VydmV5cykNCmBgYA0KDQojIyMgVE8gRE86IEFkZCByb3dzICJub3RfcmVzdXJ2IiB0byBFVkFfZGINCg0KIyMjIFRPIERPOiBJbXBsZW1lbnQgY2hvaWNlIG9mIG11bHRpcGxlIG9ic2VydmF0aW9ucyBwZXIgeWVhciBiYXNlZCBvbiBSZWxldsOpIGFyZWEgKG3CsikNCg0KIyBDb3JycmVjdGlvbiBJU1NVRSA1DQoNCkkgZGlkIG5vdCBjb3JyZWN0IGFueXRoaW5nIGFzIElsb25hIGp1c3Qgc2VudCBhIGZpbGUgY29uZmlybWluZyBpZiAob3Igbm90KSBwYXJ0IG9yIGFsbCBvZiBlYWNoIGRhdGFzZXQgY29udGFpbnMgcHJlc2VuY2UgLyBhYnNlbmNlIGRhdGEuIEZvciB1cywgdGhlIGltcG9ydGFudCB0aGluZyBpcyB0aGF0IGlmIGRhdGEgaXMgcHJlc2VuY2UgLyBhYnNlbmNlLCB0aGVyZSBpcyBubyBFVU5JUyBoYWJpdGF0IGFzc2lnbmVkIGJ5IHRoZSBFeHBlcnQgU3lzdGVtIGFuZCB0aHVzIHdlIGNhbm5vdCB1c2UgdGhlIGRhdGEgZm9yIHJlbGF0aW5nIHRvIFJTIHZhcmlhYmxlcyAoZXhjZXB0IGZvciB0aGUgZGF0YXNldCDigJxES19OYXR1cmRhdGFfUmVz4oCdIHdoZXJlIEkgaGF2ZSB1c2VkIHRoZSBBbm5leCBJIGNvZGVzIHByb3ZpZGVkIGJ5IHRoZSBjdXN0b2RpYW4gdG8gYXNzaWduIGFuIEVVTklTIGhhYml0YXQgY29kZSkuDQoNCiMgQ29ycmVjdGlvbiBJU1NVRSA2DQoNCiMjIENvcnJlY3QgY29vcmRpbmF0ZXMgZm9yIFJTX0NPREUgQ1pfMDAyOQ0KDQpUZXh0IGluIElsb25hJ3MgZW1haWw6IENaXzAwMjkgSSBoYXZlIG5vdGljZWQgdGhhdCBzb21lIGNvb3JkaW5hdGVzIGFyZSB3cm9uZ2x5IHBsYWNlZCBzbyBzb21lIG9mIGNvb3JkaW5hdGVzIHdlcmUgY29ycmVjdGVkIGxhc3QgeWVhciDigJMgSSBhbSBzZW5pbmcgeW91IG5ldyBjb29yZGluYXRlcyBmb3IgdGhlIHdob2xlIGRhdGFzZXQgKGFnYWluIHBsZWFjZSBtaW5kIHRoYXQgTG9uZy9sYXQgaXMgaW4gRERNTVNTLlNTIGZyb21hdCBub3QgaW4gZGVjaW1hbCBkZWdyZWUpLg0KDQpBdHRhY2hlZDogQ1pfMDAyOV9jb3JyZWN0ZWRjb29yZGluYXRlcy54bHN4DQoNCmBgYHtyfQ0KaXNzdWU2X0NaXzAwMjkgPC0gcmVhZF9leGNlbCgNCiAgaGVyZSgiZGF0YSIsICJyYXciLCAiRGF0YV9jb3JyZWN0aW9uc19JbG9uYSIsICJJc3N1ZTYiLA0KICAgICAgICJDWl8wMDI5X2NvcnJlY3RlZGNvb3JkaW5hdGVzLnhsc3giKSkNCmBgYA0KDQpBcHBseSB0aGUgY29udmVyc2lvbiBmdW5jdGlvbiB0byB0aGUgbG9uZ2l0dWRlIGFuZCBsYXRpdHVkZSBjb2x1bW5zOg0KDQpgYGB7cn0NCmlzc3VlNl9DWl8wMDI5IDwtIGlzc3VlNl9DWl8wMDI5ICU+JQ0KICBtdXRhdGUoTE9OR0lUVURFX2RlY2ltYWwgPSBzYXBwbHkoTE9OR0lUVURFLCBjb252ZXJ0X3RvX2RlY2ltYWwpLA0KICAgICAgICAgTEFUSVRVREVfZGVjaW1hbCA9IHNhcHBseShMQVRJVFVERSwgY29udmVydF90b19kZWNpbWFsKSkNCmBgYA0KDQpgYGB7cn0NCm5yb3coaXNzdWU2X0NaXzAwMjkpDQpucm93KGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAgICAgIGZpbHRlcihSU19DT0RFID09ICJDWl8wMDI5IikpDQpgYGANCg0KVXBkYXRlIGRiX3Jlc3Vydl91cGRhdGVkLg0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkIDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAjIENyZWF0ZSBhbiBVcGRhdGVGbGFnIHRvIG1hcmsgcm93cyB0byB1cGRhdGUNCiAgbXV0YXRlKFVwZGF0ZUZsYWcgPSBSU19DT0RFID09ICJDWl8wMDI5IikgJT4lDQogICMgSm9pbiB3aXRoIGlzc3VlNl9DWl8wMDI5IGJhc2VkIG9uIFBsb3RPYnNlcnZhdGlvbklEDQogIGxlZnRfam9pbihpc3N1ZTZfQ1pfMDAyOSAlPiUNCiAgICAgICAgICAgICAgc2VsZWN0KFBsb3RPYnNlcnZhdGlvbklELCBMT05HSVRVREVfZGVjaW1hbCwNCiAgICAgICAgICAgICAgICAgICAgIExBVElUVURFX2RlY2ltYWwpKSAlPiUNCiAgIyBVcGRhdGUgTG9uZ2l0dWRlIGFuZCBMYXRpdHVkZSBhbmQgd2l0aCB0aGUgbmV3IHZhbHVlcw0KICAjIGlmIFVwZGF0ZUZsYWcgPSBUUlVFDQogIG11dGF0ZSgNCiAgICBMb25naXR1ZGUgPSBpZl9lbHNlKFVwZGF0ZUZsYWcsIExPTkdJVFVERV9kZWNpbWFsLCBMb25naXR1ZGUpLA0KICAgIExhdGl0dWRlID0gaWZfZWxzZShVcGRhdGVGbGFnLCBMQVRJVFVERV9kZWNpbWFsLCBMYXRpdHVkZSkNCiAgICApICU+JQ0KICAjIFVwZGF0ZSBjb2x1bW4gZWRpdF9jb29yZHMgdG8gbGFiZWwgZWRpdHMNCiAgbXV0YXRlKGVkaXRfY29vcmRzID0gaWZfZWxzZShVcGRhdGVGbGFnLCBUUlVFLCBlZGl0X2Nvb3JkcykpICU+JQ0KICAjIFJlbW92ZSB1bm5lZWRlZCBjb2x1bW5zDQogIHNlbGVjdCgtVXBkYXRlRmxhZywgLUxPTkdJVFVERV9kZWNpbWFsLCAtTEFUSVRVREVfZGVjaW1hbCkgJT4lDQogICMgUmVjYWxjdWxhdGUgdXBkYXRlZCBjb29yZGluYXRlcw0KICBtdXRhdGUoTG9uX3VwZGF0ZWQgPSBpZmVsc2UoaXMubmEoTG9uX3ByZWMpLCBMb25naXR1ZGUsIExvbl9wcmVjKSwNCiAgICAgICAgIExhdF91cGRhdGVkID0gaWZlbHNlKGlzLm5hKExhdF9wcmVjKSwgTGF0aXR1ZGUsIExhdF9wcmVjKSkNCmBgYA0KDQojIyBDb3JyZWN0IENvdW50cnkgZm9yIHNvbWUgUlNfQ09ERXMNCg0KYGBge3J9DQpkYl9yZXN1cnZfdXBkYXRlZCA8LSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgIyBDcmVhdGUgYSBjb2x1bW4gZWRpdF9jb3VudHJ5IHRvIG1hcmsgcm93cyB0byB1cGRhdGUNCiAgbXV0YXRlKGVkaXRfY291bnRyeSA9IChSU19DT0RFID09ICJDWl8wMDAxIiAmIENvdW50cnkgPT0gIlBvbGFuZCIpIHwNCiAgICAgICAgICAgUlNfQ09ERSA9PSAiREVfMDAzNSIgfCBSU19DT0RFID09ICJmb3Jlc3RSRXBsb3RfRVVfMDcyIikgJT4lDQogICMgVXBkYXRlIENvdW50cnkgd2l0aCBuZXcgdmFsdWUgaWYgZWRpdF9jb3VudHJ5ID0gVFJVRQ0KICBtdXRhdGUoDQogICAgQ291bnRyeSA9IGNhc2Vfd2hlbigNCiAgICAgIGVkaXRfY291bnRyeSAmIFJTX0NPREUgPT0gIkNaXzAwMDEiICYgQ291bnRyeSA9PSAiUG9sYW5kIiB+IA0KICAgICAgICAiU2xvdmFrIFJlcHVibGljIiwNCiAgICAgIGVkaXRfY291bnRyeSAmIFJTX0NPREUgPT0gIkRFXzAwMzUiIH4gIkdlcm1hbnkiLA0KICAgICAgZWRpdF9jb3VudHJ5ICYgUlNfQ09ERSA9PSAiZm9yZXN0UkVwbG90X0VVXzA3MiIgfiAiVWtyYWluZSIsDQogICAgICBUUlVFIH4gQ291bnRyeSkNCiAgICApDQpgYGANCg0KIyMgQVRfMDAwNGMNCg0KTG9uZ2l0dWRlIHdhcyB3cm9uZywgY29ycmVjdC4NCg0KYGBge3J9DQpkYl9yZXN1cnZfdXBkYXRlZCA8LSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgIyBDcmVhdGUgYW4gVXBkYXRlRmxhZyB0byBtYXJrIHJvd3MgdG8gdXBkYXRlDQogIG11dGF0ZShVcGRhdGVGbGFnID0gUGxvdE9ic2VydmF0aW9uSUQgPT0gNDQyOSB8IFBsb3RPYnNlcnZhdGlvbklEID09IDQ1MTkpICU+JQ0KICAjIFVwZGF0ZSBMb25naXR1ZGUgd2l0aCB0aGUgbmV3IHZhbHVlcw0KICAjIGlmIFVwZGF0ZUZsYWcgPSBUUlVFDQogIG11dGF0ZShMb25naXR1ZGUgPSBpZl9lbHNlKFVwZGF0ZUZsYWcsIDEzLjY4MTk4MSwgTG9uZ2l0dWRlKSkgJT4lDQogICMgVXBkYXRlIGNvbHVtbiBlZGl0X2Nvb3JkcyB0byBsYWJlbCBlZGl0cw0KICBtdXRhdGUoZWRpdF9jb29yZHMgPSBpZl9lbHNlKFVwZGF0ZUZsYWcsIFRSVUUsIGVkaXRfY29vcmRzKSkgJT4lDQogICMgUmVtb3ZlIHVubmVlZGVkIGNvbHVtbnMNCiAgc2VsZWN0KC1VcGRhdGVGbGFnKSAgJT4lDQogICMgUmVjYWxjdWxhdGUgdXBkYXRlZCBjb29yZGluYXRlcw0KICBtdXRhdGUoTG9uX3VwZGF0ZWQgPSBpZmVsc2UoaXMubmEoTG9uX3ByZWMpLCBMb25naXR1ZGUsIExvbl9wcmVjKSwNCiAgICAgICAgIExhdF91cGRhdGVkID0gaWZlbHNlKGlzLm5hKExhdF9wcmVjKSwgTGF0aXR1ZGUsIExhdF9wcmVjKSkNCmBgYA0KDQojIyBDWl8wMDE5XzAxMA0KDQpMYXRpdHVkZSB3YXMgd3JvbmcsIGNvcnJlY3QuDQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X3VwZGF0ZWQgPC0gZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogICMgQ3JlYXRlIGFuIFVwZGF0ZUZsYWcgdG8gbWFyayByb3dzIHRvIHVwZGF0ZQ0KICBtdXRhdGUoVXBkYXRlRmxhZyA9IFBsb3RPYnNlcnZhdGlvbklEID09IDY0MDAgfCBQbG90T2JzZXJ2YXRpb25JRCA9PSA2NDAxIHwNCiAgICAgICAgICAgUGxvdE9ic2VydmF0aW9uSUQgPT0gNjQwMikgJT4lDQogICMgVXBkYXRlIExhdGl0dWRlIHdpdGggdGhlIG5ldyB2YWx1ZXMNCiAgIyBpZiBVcGRhdGVGbGFnID0gVFJVRQ0KICBtdXRhdGUoTGF0aXR1ZGUgPSBpZl9lbHNlKFVwZGF0ZUZsYWcsIDUwLjE0MDU1NTk5OTk5OTk5NywgTGF0aXR1ZGUpKSAlPiUNCiAgIyBVcGRhdGUgY29sdW1uIGVkaXRfY29vcmRzIHRvIGxhYmVsIGVkaXRzDQogIG11dGF0ZShlZGl0X2Nvb3JkcyA9IGlmX2Vsc2UoVXBkYXRlRmxhZywgVFJVRSwgZWRpdF9jb29yZHMpKSAlPiUNCiAgIyBSZW1vdmUgdW5uZWVkZWQgY29sdW1ucw0KICBzZWxlY3QoLVVwZGF0ZUZsYWcpICAlPiUNCiAgIyBSZWNhbGN1bGF0ZSB1cGRhdGVkIGNvb3JkaW5hdGVzDQogIG11dGF0ZShMb25fdXBkYXRlZCA9IGlmZWxzZShpcy5uYShMb25fcHJlYyksIExvbmdpdHVkZSwgTG9uX3ByZWMpLA0KICAgICAgICAgTGF0X3VwZGF0ZWQgPSBpZmVsc2UoaXMubmEoTGF0X3ByZWMpLCBMYXRpdHVkZSwgTGF0X3ByZWMpKQ0KYGBgDQoNCiMjIElUXzAwMDgNCg0KTG9uZ2l0dWRlIGFuZCBsYXRpdHVkZSB3ZXJlIHdyb25nLCBjb3JyZWN0Lg0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkIDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAjIENyZWF0ZSBhbiBVcGRhdGVGbGFnIHRvIG1hcmsgcm93cyB0byB1cGRhdGUNCiAgbXV0YXRlKFVwZGF0ZUZsYWcgPSBQbG90T2JzZXJ2YXRpb25JRCA9PSAzNDA0MjYgfA0KICAgICAgICAgICBQbG90T2JzZXJ2YXRpb25JRCA9PSAzNDA0MjcpICU+JQ0KICAjIFVwZGF0ZSBMb25naXR1ZGUgYW5kIExhdGl0dWRlIHdpdGggdGhlIG5ldyB2YWx1ZXMNCiAgIyBpZiBVcGRhdGVGbGFnID0gVFJVRQ0KICBtdXRhdGUoTG9uZ2l0dWRlID0gaWZfZWxzZShVcGRhdGVGbGFnLCAxNC44NDMwMzMwNSwgTG9uZ2l0dWRlKSwNCiAgICAgICAgIExhdGl0dWRlID0gaWZfZWxzZShVcGRhdGVGbGFnLCA0Mi4wOTY2NzYyLCBMYXRpdHVkZSkpICU+JQ0KICAjIFVwZGF0ZSBjb2x1bW4gZWRpdF9jb29yZHMgdG8gbGFiZWwgZWRpdHMNCiAgbXV0YXRlKGVkaXRfY29vcmRzID0gaWZfZWxzZShVcGRhdGVGbGFnLCBUUlVFLCBlZGl0X2Nvb3JkcykpICU+JQ0KICAjIFJlbW92ZSB1bm5lZWRlZCBjb2x1bW5zDQogIHNlbGVjdCgtVXBkYXRlRmxhZykgICU+JQ0KICAjIFJlY2FsY3VsYXRlIHVwZGF0ZWQgY29vcmRpbmF0ZXMNCiAgbXV0YXRlKExvbl91cGRhdGVkID0gaWZlbHNlKGlzLm5hKExvbl9wcmVjKSwgTG9uZ2l0dWRlLCBMb25fcHJlYyksDQogICAgICAgICBMYXRfdXBkYXRlZCA9IGlmZWxzZShpcy5uYShMYXRfcHJlYyksIExhdGl0dWRlLCBMYXRfcHJlYykpDQpgYGANCg0KIyMgQVRfMDAwNw0KDQpgYGB7cn0NCmlzc3VlNl9BVF8wMDA3IDwtIHJlYWRfZXhjZWwoDQogIGhlcmUoImRhdGEiLCAicmF3IiwgIkRhdGFfY29ycmVjdGlvbnNfSWxvbmEiLCAiSXNzdWU2IiwNCiAgICAgICAiSXNzdWU2X25ld2NvcnJlY3Rpb25zIiwgIklzc3VlNl9BVF8wMDA3Y29ycmVjdGVkLnhsc3giKSkNCmBgYA0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkIDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAjIENyZWF0ZSBhbiBVcGRhdGVGbGFnIHRvIG1hcmsgcm93cyB0byB1cGRhdGUNCiAgbXV0YXRlKFVwZGF0ZUZsYWcgPSBQbG90T2JzZXJ2YXRpb25JRCAlaW4lIGlzc3VlNl9BVF8wMDA3JHBsb3RJRCkgJT4lDQogICMgSm9pbiB3aXRoIGlzc3VlNl9BVF8wMDA3IGJhc2VkIG9uIFBsb3RPYnNlcnZhdGlvbklEDQogIGxlZnRfam9pbihpc3N1ZTZfQVRfMDAwNyAlPiUNCiAgICAgICAgICAgICAgbXV0YXRlKFBsb3RPYnNlcnZhdGlvbklEID0gcGxvdElEKSAlPiUNCiAgICAgICAgICAgICAgc2VsZWN0KFBsb3RPYnNlcnZhdGlvbklELCBjb3VudHJ5Y29kZV9uZXcpKSAlPiUNCiAgIyBVcGRhdGUgQ291bnRyeSBhbmQgd2l0aCB0aGUgbmV3IHZhbHVlcw0KICAjIGlmIFVwZGF0ZUZsYWcgPSBUUlVFDQogIG11dGF0ZShDb3VudHJ5ID0gaWZfZWxzZShVcGRhdGVGbGFnLCBjb3VudHJ5Y29kZV9uZXcsIENvdW50cnkpKSAlPiUNCiAgbXV0YXRlKENvdW50cnkgPSBjYXNlX3doZW4oDQogICAgQ291bnRyeSA9PSAiQVQiIH4gIkF1c3RyaWEiLA0KICAgIENvdW50cnkgPT0gIlNJIiB+ICJTbG92ZW5pYSIsDQogICAgQ291bnRyeSA9PSAiQ0giIH4gIlN3aXR6ZXJsYW5kIiwNCiAgICBDb3VudHJ5ID09ICJERSIgfiAiR2VybWFueSIsDQogICAgQ291bnRyeSA9PSAiSVQiIH4gIkl0YWx5IiwNCiAgICBUUlVFIH4gQ291bnRyeSkpICU+JQ0KICAjIFVwZGF0ZSBjb2x1bW4gZWRpdF9jb3VudHJ5IHRvIGxhYmVsIGVkaXRzDQogIG11dGF0ZShlZGl0X2NvdW50cnkgPSBpZl9lbHNlKFVwZGF0ZUZsYWcsIFRSVUUsIGVkaXRfY291bnRyeSkpICU+JQ0KICAjIFJlbW92ZSB1bm5lZWRlZCBjb2x1bW5zDQogIHNlbGVjdCgtVXBkYXRlRmxhZywgLWNvdW50cnljb2RlX25ldykNCmBgYA0KDQojIyBDSF8wMDAyDQoNClRleHQgaW4gSWxvbmEncyBlbWFpbDogQ0hfMDAwMiBjb3JyZWN0ZWQgY29vcmRpbmF0ZXMuDQoNCmBgYHtyfQ0KaXNzdWU2X0NIXzAwMDIgPC0gcmVhZF9leGNlbCgNCiAgaGVyZSgiZGF0YSIsICJyYXciLCAiRGF0YV9jb3JyZWN0aW9uc19JbG9uYSIsICJJc3N1ZTYiLA0KICAgICAgICJJc3N1ZTZfbmV3Y29ycmVjdGlvbnMiLCAiSXNzdWU2X0NIXzAwMDJfY29ycmVjdGVkY29vcmRpbmF0ZXMueGxzeCIpKQ0KYGBgDQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X3VwZGF0ZWQgPC0gZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogICMgQ3JlYXRlIGFuIFVwZGF0ZUZsYWcgdG8gbWFyayByb3dzIHRvIHVwZGF0ZQ0KICBtdXRhdGUoVXBkYXRlRmxhZyA9IFBsb3RPYnNlcnZhdGlvbklEICVpbiUNCiAgICAgICAgICAgaXNzdWU2X0NIXzAwMDIkUGxvdE9ic2VydmF0aW9uSUQpICU+JQ0KICAjIEpvaW4gd2l0aCBpc3N1ZTZfQ0hfMDAwMiBiYXNlZCBvbiBQbG90T2JzZXJ2YXRpb25JRA0KICBsZWZ0X2pvaW4oaXNzdWU2X0NIXzAwMDIgJT4lDQogICAgICAgICAgICAgIHNlbGVjdChQbG90T2JzZXJ2YXRpb25JRCwgTE9OR0lUVURFLCBMQVRJVFVERSkpICU+JQ0KICAjIFVwZGF0ZSBjb29yZGluYXRlcyBhbmQgd2l0aCB0aGUgbmV3IHZhbHVlcw0KICAjIGlmIFVwZGF0ZUZsYWcgPSBUUlVFDQogIG11dGF0ZShMb25naXR1ZGUgPSBpZl9lbHNlKFVwZGF0ZUZsYWcsIExPTkdJVFVERSwgTG9uZ2l0dWRlKSwNCiAgICAgICAgIExhdGl0dWRlID0gaWZfZWxzZShVcGRhdGVGbGFnLCBMQVRJVFVERSwgTGF0aXR1ZGUpKSAlPiUNCiAgIyBVcGRhdGUgY29sdW1uIGVkaXRfY29vcmRzIHRvIGxhYmVsIGVkaXRzDQogIG11dGF0ZShlZGl0X2Nvb3JkcyA9IGlmX2Vsc2UoVXBkYXRlRmxhZywgVFJVRSwgZWRpdF9jb29yZHMpKSAlPiUNCiAgIyBSZWNhbGN1bGF0ZSB1cGRhdGVkIGNvb3JkaW5hdGVzDQogIG11dGF0ZShMb25fdXBkYXRlZCA9IGlmZWxzZShpcy5uYShMb25fcHJlYyksIExvbmdpdHVkZSwgTG9uX3ByZWMpLA0KICAgICAgICAgTGF0X3VwZGF0ZWQgPSBpZmVsc2UoaXMubmEoTGF0X3ByZWMpLCBMYXRpdHVkZSwgTGF0X3ByZWMpKSAlPiUNCiAgIyBSZW1vdmUgdW5uZWVkZWQgY29sdW1ucw0KICBzZWxlY3QoLVVwZGF0ZUZsYWcsIC1MT05HSVRVREUsIC1MQVRJVFVERSkNCmBgYA0KDQojIyBMVF8wMDAxDQoNClRleHQgaW4gSWxvbmEncyBlbWFpbDogTFRfMDAwMSDigJMgYWdhaW4gY29ycmVjdGVkIGNvb3JkaW5hdGVzIOKAkyBhbGwgcGxvdHMgaW4gTFQuDQoNCmBgYHtyfQ0KaXNzdWU2X0xUXzAwMDEgPC0gcmVhZF9leGNlbCgNCiAgaGVyZSgiZGF0YSIsICJyYXciLCAiRGF0YV9jb3JyZWN0aW9uc19JbG9uYSIsICJJc3N1ZTYiLA0KICAgICAgICJJc3N1ZTZfbmV3Y29ycmVjdGlvbnMiLCAiSXNzdWU2X0xUXzAwMDFfY29ycmVjdGVkY29vcmQueGxzeCIpKQ0KYGBgDQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X3VwZGF0ZWQgPC0gZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogICMgQ3JlYXRlIGFuIFVwZGF0ZUZsYWcgdG8gbWFyayByb3dzIHRvIHVwZGF0ZQ0KICBtdXRhdGUoVXBkYXRlRmxhZyA9IFBsb3RPYnNlcnZhdGlvbklEICVpbiUNCiAgICAgICAgICAgaXNzdWU2X0xUXzAwMDEkUGxvdE9ic2VydmF0aW9uSUQpICU+JQ0KICAjIEpvaW4gd2l0aCBpc3N1ZTZfTFRfMDAwMSBiYXNlZCBvbiBQbG90T2JzZXJ2YXRpb25JRA0KICBsZWZ0X2pvaW4oaXNzdWU2X0xUXzAwMDEgJT4lDQogICAgICAgICAgICAgIHNlbGVjdChQbG90T2JzZXJ2YXRpb25JRCwgQ2hlY2tlZF9MYXQsIENoZWNrZWRfTG9uKSkgJT4lDQogICMgVXBkYXRlIGNvb3JkaW5hdGVzIHdpdGggdGhlIG5ldyB2YWx1ZXMNCiAgIyBpZiBVcGRhdGVGbGFnID0gVFJVRQ0KICBtdXRhdGUoTG9uZ2l0dWRlID0gaWZfZWxzZShVcGRhdGVGbGFnLCBDaGVja2VkX0xvbiwgTG9uZ2l0dWRlKSwNCiAgICAgICAgIExhdGl0dWRlID0gaWZfZWxzZShVcGRhdGVGbGFnLCBDaGVja2VkX0xhdCwgTGF0aXR1ZGUpKSAlPiUNCiAgIyBVcGRhdGUgY29sdW1uIGVkaXRfY29vcmRzIHRvIGxhYmVsIGVkaXRzDQogIG11dGF0ZShlZGl0X2Nvb3JkcyA9IGlmX2Vsc2UoVXBkYXRlRmxhZywgVFJVRSwgZWRpdF9jb29yZHMpKSAlPiUNCiAgIyBSZWNhbGN1bGF0ZSB1cGRhdGVkIGNvb3JkaW5hdGVzDQogIG11dGF0ZShMb25fdXBkYXRlZCA9IGlmZWxzZShpcy5uYShMb25fcHJlYyksIExvbmdpdHVkZSwgTG9uX3ByZWMpLA0KICAgICAgICAgTGF0X3VwZGF0ZWQgPSBpZmVsc2UoaXMubmEoTGF0X3ByZWMpLCBMYXRpdHVkZSwgTGF0X3ByZWMpKSAlPiUNCiAgIyBSZW1vdmUgdW5uZWVkZWQgY29sdW1ucw0KICBzZWxlY3QoLVVwZGF0ZUZsYWcsIC1DaGVja2VkX0xhdCwgLUNoZWNrZWRfTG9uKQ0KYGBgDQoNCiMjIERFXzAwMzdfMDc0DQoNClRleHQgaW4gSWxvbmEncyBlbWFpbDogUGxlYXNlIG1pbmQgdGhhdCBwcmVjaXNlIGNvb3JkaW5hdGVzIGFyZSBwcml2YXRlLiBVdGUgZ2F2ZSBtZSBYIGFuZCBZIHZhbHVlcyB3aXRoIGluZm8gYWJvdXQgY29vcmRpbmF0ZXMgc3lzdGVtIC0gYWNjb3JkaW5nIGhlciBpdCBpcyAgRVRSU18xOTg5X1VUTV9ab25lXzMyTi4gQWNjb3JkaW5nIG1lIHggYW5kIHkgYXJlIGluIFBELzgzIC8gMy1kZWdyZWUgR2F1c3MtS3J1Z2VyIHpvbmUgMyAoRS1OKSAtIEVQU0c6NTY2Ni4gU08gSSBtb2RpZmllZCB0aGVtIGludG8gd2dzODQgKHZhbHVlcyBpbiBsb25naXR1ZGUvbGF0aXR1ZGUpIOKAkyBJIGFtIG5vdCBHSVMgZ3V5IHNvIEkganVzdCB1c2VkIEFSQ0dJUyBQcm8gZm9yIHByb2plY3Rpb24g4oCTIHBsZWFzZSBjaGVjayBidXQgdGhlIHBsb3RzIG5vdyBhcmUgbG9jYXRlZCBvbiB0aGUgY29ycmVjdCBzaWRlIG9mIHJpdmVyIGluIEdlcm1hbnkuDQoNCkkgdG9vayB0aGUgY29yZGluYXRlcyBhcyBPSyB3aXRob3V0IGZ1cnRoZXIgY2hlY2tpbmcuDQoNCmBgYHtyfQ0KaXNzdWU2X0RFXzAwMzdfMDc0IDwtIHJlYWRfZXhjZWwoDQogIGhlcmUoImRhdGEiLCAicmF3IiwgIkRhdGFfY29ycmVjdGlvbnNfSWxvbmEiLCAiSXNzdWU2IiwNCiAgICAgICAiSXNzdWU2X25ld2NvcnJlY3Rpb25zIiwgIklzc3VlNl9ERV8wMDM3Xzc0X2NvcnJlY3RlZGZpbi54bHN4IikpDQpgYGANCg0KYGBge3J9DQpkYl9yZXN1cnZfdXBkYXRlZCA8LSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgIyBDcmVhdGUgYW4gVXBkYXRlRmxhZyB0byBtYXJrIHJvd3MgdG8gdXBkYXRlDQogIG11dGF0ZShVcGRhdGVGbGFnID0gUGxvdE9ic2VydmF0aW9uSUQgJWluJQ0KICAgICAgICAgICBpc3N1ZTZfREVfMDAzN18wNzQkUGxvdE9ic2VydmF0aW9uSUQpICU+JQ0KICAjIEpvaW4gd2l0aCBpc3N1ZTZfREVfMDAzN18wNzQgYmFzZWQgb24gUGxvdE9ic2VydmF0aW9uSUQNCiAgbGVmdF9qb2luKGlzc3VlNl9ERV8wMDM3XzA3NCAlPiUNCiAgICAgICAgICAgICAgbXV0YXRlKExvbmdpdHVkZV9uZXcgPSBMb25naXR1ZGUsIExhdGl0dWRlX25ldyA9IExhdGl0dWRlKSAlPiUNCiAgICAgICAgICAgICAgc2VsZWN0KFBsb3RPYnNlcnZhdGlvbklELCBMb25naXR1ZGVfbmV3LCBMYXRpdHVkZV9uZXcpKSAlPiUNCiAgIyBVcGRhdGUgY29vcmRpbmF0ZXMgd2l0aCB0aGUgbmV3IHZhbHVlcw0KICAjIGlmIFVwZGF0ZUZsYWcgPSBUUlVFDQogIG11dGF0ZShMb25naXR1ZGUgPSBpZl9lbHNlKFVwZGF0ZUZsYWcsIExvbmdpdHVkZV9uZXcsIExvbmdpdHVkZSksDQogICAgICAgICBMYXRpdHVkZSA9IGlmX2Vsc2UoVXBkYXRlRmxhZywgTGF0aXR1ZGVfbmV3LCBMYXRpdHVkZSkpICU+JQ0KICAjIFVwZGF0ZSBjb2x1bW4gZWRpdF9jb29yZHMgdG8gbGFiZWwgZWRpdHMNCiAgbXV0YXRlKGVkaXRfY29vcmRzID0gaWZfZWxzZShVcGRhdGVGbGFnLCBUUlVFLCBlZGl0X2Nvb3JkcykpICU+JQ0KICAjIFJlY2FsY3VsYXRlIHVwZGF0ZWQgY29vcmRpbmF0ZXMNCiAgbXV0YXRlKExvbl91cGRhdGVkID0gaWZlbHNlKGlzLm5hKExvbl9wcmVjKSwgTG9uZ2l0dWRlLCBMb25fcHJlYyksDQogICAgICAgICBMYXRfdXBkYXRlZCA9IGlmZWxzZShpcy5uYShMYXRfcHJlYyksIExhdGl0dWRlLCBMYXRfcHJlYykpICU+JQ0KICAjIFJlbW92ZSB1bm5lZWRlZCBjb2x1bW5zDQogIHNlbGVjdCgtVXBkYXRlRmxhZywgLUxvbmdpdHVkZV9uZXcsIC1MYXRpdHVkZV9uZXcpDQpgYGANCg0KIyMgQ2hlY2sgaWYgQ291bnRyeSBpcyBjb3JyZWN0DQoNCkNoZWNrIGlmIENvdW50cnkgaXMgY29ycmVjdCBkaXJlY3RseSBpbiBSIChub3QgQXJjR0lTKS4NCg0KYGBge3J9DQojIExvYWQgd29ybGQgYm91bmRhcmllcw0Kd29ybGQgPC0gbmVfY291bnRyaWVzKHNjYWxlID0gIm1lZGl1bSIsIHJldHVybmNsYXNzID0gInNmIikNCg0KIyBDb252ZXJ0IHBvaW50cyB0byBhbiBzZiBvYmplY3QNCnBvaW50cyA8LSBzdF9hc19zZihkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgICAgICAgICAgICAgICAgICAgIGZpbHRlcighaXMubmEoTG9uX3VwZGF0ZWQpICYgIWlzLm5hKExhdF91cGRhdGVkKSksDQogICAgICAgICAgICAgICAgICAgY29vcmRzID0gYygiTG9uX3VwZGF0ZWQiLCAiTGF0X3VwZGF0ZWQiKSwgY3JzID0gNDMyNikNCg0KIyBQZXJmb3JtIHNwYXRpYWwgam9pbiB0byBmaW5kIHRoZSBjb3VudHJ5DQpwb2ludHNfd2l0aF9jb3VudHJ5IDwtIHN0X2pvaW4ocG9pbnRzLCB3b3JsZCwgam9pbiA9IHN0X3dpdGhpbiwgbGVmdCA9IFQpDQpgYGANCg0KYGBge3J9DQojIENvbXBhcmUgY291bnRyaWVzDQpkYl9yZXN1cnZfdXBkYXRlZF9jb3VudHJ5IDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICBsZWZ0X2pvaW4ocG9pbnRzX3dpdGhfY291bnRyeSAlPiUNCiAgICAgICAgICAgICAgc2VsZWN0KFBsb3RPYnNlcnZhdGlvbklELCBzb3ZlcmVpZ250KSwNCiAgICAgICAgICAgIGJ5ID0gIlBsb3RPYnNlcnZhdGlvbklEIikgJT4lDQogIG11dGF0ZShnZW9jb2RlZF9jb3VudHJ5ID0gaWZfZWxzZSgNCiAgICBpcy5uYShMb25fdXBkYXRlZCkgfCBpcy5uYShMYXRfdXBkYXRlZCksIA0KICAgIE5BX2NoYXJhY3Rlcl8sIHNvdmVyZWlnbnQpKSAlPiUNCiAgc2VsZWN0KC1zb3ZlcmVpZ250LCAtIGdlb21ldHJ5KSAlPiUNCiAgIyBDaGFuZ2UgdGhlIG5hbWVzIG9mIHNvbWUgY291bnRyaWVzIGZvciBtYXRjaGluZw0KICBtdXRhdGUoZ2VvY29kZWRfY291bnRyeSA9IGlmX2Vsc2UoZ2VvY29kZWRfY291bnRyeSA9PSAiQ3plY2hpYSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ3plY2ggUmVwdWJsaWMiLCBnZW9jb2RlZF9jb3VudHJ5KSkgJT4lDQogIG11dGF0ZShnZW9jb2RlZF9jb3VudHJ5ID0gaWZfZWxzZShnZW9jb2RlZF9jb3VudHJ5ID09ICJTbG92YWtpYSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU2xvdmFrIFJlcHVibGljIiwgZ2VvY29kZWRfY291bnRyeSkpDQpkYl9yZXN1cnZfdXBkYXRlZF9jb3VudHJ5JGNvdW50cnlfY29ycmVjdCA8LQ0KICBkYl9yZXN1cnZfdXBkYXRlZF9jb3VudHJ5JENvdW50cnkgPT0NCiAgZGJfcmVzdXJ2X3VwZGF0ZWRfY291bnRyeSRnZW9jb2RlZF9jb3VudHJ5DQpkYl9yZXN1cnZfdXBkYXRlZCA8LSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgbGVmdF9qb2luKGRiX3Jlc3Vydl91cGRhdGVkX2NvdW50cnkgJT4lDQogICAgICAgICAgICAgIHNlbGVjdChQbG90T2JzZXJ2YXRpb25JRCwgZ2VvY29kZWRfY291bnRyeSwgY291bnRyeV9jb3JyZWN0KSkNCmBgYA0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkICU+JSBjb3VudChjb3VudHJ5X2NvcnJlY3QpDQpgYGANCg0KU2hvdyBtYXAgd2l0aCBwb2ludHMgd2hlcmUgY291bnRyeV9jb3JyZWN0IGlzIEZBTFNFLg0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIHRoZSBleHRlbnQgb2YgdGhlIHBvaW50cw0KcG9pbnRzX2V4dGVudCA8LSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgZmlsdGVyKGNvdW50cnlfY29ycmVjdCA9PSBGQUxTRSkgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBsb25fbWluID0gbWluKExvbl91cGRhdGVkLCBuYS5ybSA9IFRSVUUpLA0KICAgIGxvbl9tYXggPSBtYXgoTG9uX3VwZGF0ZWQsIG5hLnJtID0gVFJVRSksDQogICAgbGF0X21pbiA9IG1pbihMYXRfdXBkYXRlZCwgbmEucm0gPSBUUlVFKSwNCiAgICBsYXRfbWF4ID0gbWF4KExhdF91cGRhdGVkLCBuYS5ybSA9IFRSVUUpDQogICkNCg0KIyBBZGQgcGFkZGluZyB0byB0aGUgZXh0ZW50IChhZGp1c3QgYXMgbmVlZGVkKQ0KcGFkZGluZyA8LSAwLjUgICMgQWRqdXN0IHBhZGRpbmcgdG8geW91ciBwcmVmZXJlbmNlDQp4X2xpbWl0cyA8LSBjKHBvaW50c19leHRlbnQkbG9uX21pbiAtIHBhZGRpbmcsIHBvaW50c19leHRlbnQkbG9uX21heCArIHBhZGRpbmcpDQp5X2xpbWl0cyA8LSBjKHBvaW50c19leHRlbnQkbGF0X21pbiAtIHBhZGRpbmcsIHBvaW50c19leHRlbnQkbGF0X21heCArIHBhZGRpbmcpDQoNCiMgQ29tcHV0ZSBjZW50cm9pZHMgZm9yIGxhYmVsaW5nDQp3b3JsZF9jZW50cm9pZHMgPC0gd29ybGQgJT4lDQogIG11dGF0ZShjZW50cm9pZCA9IHN0X2NlbnRyb2lkKGdlb21ldHJ5KSkgJT4lDQogIG11dGF0ZShsb24gPSBzdF9jb29yZGluYXRlcyhjZW50cm9pZClbLCAxXSwNCiAgICAgICAgIGxhdCA9IHN0X2Nvb3JkaW5hdGVzKGNlbnRyb2lkKVssIDJdKQ0KDQojIENyZWF0ZSB0aGUgem9vbWVkIG1hcA0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSB3b3JsZCwgZmlsbCA9ICJsaWdodGJsdWUiLCBjb2xvciA9ICJncmF5IikgKw0KICBnZW9tX3BvaW50KGRhdGEgPSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgICAgICAgICAgICAgIGZpbHRlcihjb3VudHJ5X2NvcnJlY3QgPT0gRkFMU0UpLA0KICAgICAgICAgICAgIGFlcyh4ID0gTG9uX3VwZGF0ZWQsIHkgPSBMYXRfdXBkYXRlZCwgY29sb3IgPSBDb3VudHJ5KSwNCiAgICAgICAgICAgICBzaXplID0gMikgKw0KICBnZW9tX3RleHQoZGF0YSA9IHdvcmxkX2NlbnRyb2lkcywgDQogICAgICAgICAgICBhZXMoeCA9IGxvbiwgeSA9IGxhdCwgbGFiZWwgPSBzb3ZlcmVpZ250KSwNCiAgICAgICAgICAgIHNpemUgPSAzLCBjb2xvciA9ICJibGFjayIpICsgICMgQWRqdXN0IHNpemUgYW5kIGNvbG9yIGFzIG5lZWRlZA0KICBjb29yZF9zZih4bGltID0geF9saW1pdHMsIHlsaW0gPSB5X2xpbWl0cykgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQpTaG93IG1hcCB3aXRoIHBvaW50cyB3aGVyZSBjb3VudHJ5X2NvcnJlY3QgaXMgTkEuDQoNCmBgYHtyfQ0KIyBDYWxjdWxhdGUgdGhlIGV4dGVudCBvZiB0aGUgcG9pbnRzDQpwb2ludHNfZXh0ZW50IDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICBmaWx0ZXIoaXMubmEoY291bnRyeV9jb3JyZWN0KSkgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBsb25fbWluID0gbWluKExvbl91cGRhdGVkLCBuYS5ybSA9IFRSVUUpLA0KICAgIGxvbl9tYXggPSBtYXgoTG9uX3VwZGF0ZWQsIG5hLnJtID0gVFJVRSksDQogICAgbGF0X21pbiA9IG1pbihMYXRfdXBkYXRlZCwgbmEucm0gPSBUUlVFKSwNCiAgICBsYXRfbWF4ID0gbWF4KExhdF91cGRhdGVkLCBuYS5ybSA9IFRSVUUpDQogICkNCg0KIyBBZGQgcGFkZGluZyB0byB0aGUgZXh0ZW50IChhZGp1c3QgYXMgbmVlZGVkKQ0KcGFkZGluZyA8LSAwLjUgICMgQWRqdXN0IHBhZGRpbmcgdG8geW91ciBwcmVmZXJlbmNlDQp4X2xpbWl0cyA8LSBjKHBvaW50c19leHRlbnQkbG9uX21pbiAtIHBhZGRpbmcsIHBvaW50c19leHRlbnQkbG9uX21heCArIHBhZGRpbmcpDQp5X2xpbWl0cyA8LSBjKHBvaW50c19leHRlbnQkbGF0X21pbiAtIHBhZGRpbmcsIHBvaW50c19leHRlbnQkbGF0X21heCArIHBhZGRpbmcpDQoNCiMgQ3JlYXRlIHRoZSB6b29tZWQgbWFwDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHdvcmxkLCBmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG9yID0gImdyYXkiKSArDQogIGdlb21fcG9pbnQoZGF0YSA9IGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAgICAgICAgICAgICAgZmlsdGVyKGlzLm5hKGNvdW50cnlfY29ycmVjdCkpLA0KICAgICAgICAgICAgIGFlcyh4ID0gTG9uX3VwZGF0ZWQsIHkgPSBMYXRfdXBkYXRlZCwgY29sb3IgPSBDb3VudHJ5KSwNCiAgICAgICAgICAgICBzaXplID0gMikgKw0KICBnZW9tX3RleHQoZGF0YSA9IHdvcmxkX2NlbnRyb2lkcywgDQogICAgICAgICAgICBhZXMoeCA9IGxvbiwgeSA9IGxhdCwgbGFiZWwgPSBzb3ZlcmVpZ250KSwNCiAgICAgICAgICAgIHNpemUgPSAzLCBjb2xvciA9ICJibGFjayIpICsgICMgQWRqdXN0IHNpemUgYW5kIGNvbG9yIGFzIG5lZWRlZA0KICBjb29yZF9zZih4bGltID0geF9saW1pdHMsIHlsaW0gPSB5X2xpbWl0cykgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQpQb2ludHMgd2hlcmUgY291bnRyeV9jb3JyZWN0IGlzIE5BIGFuZCBDb3VudHJ5ICA9PSAiVW5pdGVkIEtpbmdkb20iLg0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIHRoZSBleHRlbnQgb2YgdGhlIHBvaW50cw0KcG9pbnRzX2V4dGVudCA8LSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgZmlsdGVyKGlzLm5hKGNvdW50cnlfY29ycmVjdCkgJiBDb3VudHJ5ID09ICJVbml0ZWQgS2luZ2RvbSIpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgbG9uX21pbiA9IG1pbihMb25fdXBkYXRlZCwgbmEucm0gPSBUUlVFKSwNCiAgICBsb25fbWF4ID0gbWF4KExvbl91cGRhdGVkLCBuYS5ybSA9IFRSVUUpLA0KICAgIGxhdF9taW4gPSBtaW4oTGF0X3VwZGF0ZWQsIG5hLnJtID0gVFJVRSksDQogICAgbGF0X21heCA9IG1heChMYXRfdXBkYXRlZCwgbmEucm0gPSBUUlVFKQ0KICApDQoNCiMgQWRkIHBhZGRpbmcgdG8gdGhlIGV4dGVudCAoYWRqdXN0IGFzIG5lZWRlZCkNCnBhZGRpbmcgPC0gMC41ICAjIEFkanVzdCBwYWRkaW5nIHRvIHlvdXIgcHJlZmVyZW5jZQ0KeF9saW1pdHMgPC0gYyhwb2ludHNfZXh0ZW50JGxvbl9taW4gLSBwYWRkaW5nLCBwb2ludHNfZXh0ZW50JGxvbl9tYXggKyBwYWRkaW5nKQ0KeV9saW1pdHMgPC0gYyhwb2ludHNfZXh0ZW50JGxhdF9taW4gLSBwYWRkaW5nLCBwb2ludHNfZXh0ZW50JGxhdF9tYXggKyBwYWRkaW5nKQ0KDQojIENyZWF0ZSB0aGUgem9vbWVkIG1hcA0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSB3b3JsZCwgZmlsbCA9ICJsaWdodGJsdWUiLCBjb2xvciA9ICJncmF5IikgKw0KICBnZW9tX3BvaW50KGRhdGEgPSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgICAgICAgICAgICAgIGZpbHRlcihpcy5uYShjb3VudHJ5X2NvcnJlY3QpICYgQ291bnRyeSA9PSAiVW5pdGVkIEtpbmdkb20iKSwNCiAgICAgICAgICAgICBhZXMoeCA9IExvbl91cGRhdGVkLCB5ID0gTGF0X3VwZGF0ZWQsIGNvbG9yID0gQ291bnRyeSksDQogICAgICAgICAgICAgc2l6ZSA9IDIpICsNCiAgZ2VvbV90ZXh0KGRhdGEgPSB3b3JsZF9jZW50cm9pZHMsIA0KICAgICAgICAgICAgYWVzKHggPSBsb24sIHkgPSBsYXQsIGxhYmVsID0gc292ZXJlaWdudCksDQogICAgICAgICAgICBzaXplID0gMywgY29sb3IgPSAiYmxhY2siKSArICAjIEFkanVzdCBzaXplIGFuZCBjb2xvciBhcyBuZWVkZWQNCiAgY29vcmRfc2YoeGxpbSA9IHhfbGltaXRzLCB5bGltID0geV9saW1pdHMpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KUG9pbnRzIHdoZXJlIGNvdW50cnlfY29ycmVjdCBpcyBOQSBhbmQgQ291bnRyeSAgPT0gIkRlbm1hcmsiLg0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIHRoZSBleHRlbnQgb2YgdGhlIHBvaW50cw0KcG9pbnRzX2V4dGVudCA8LSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgZmlsdGVyKGlzLm5hKGNvdW50cnlfY29ycmVjdCkgJiBDb3VudHJ5ID09ICJEZW5tYXJrIikgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBsb25fbWluID0gbWluKExvbl91cGRhdGVkLCBuYS5ybSA9IFRSVUUpLA0KICAgIGxvbl9tYXggPSBtYXgoTG9uX3VwZGF0ZWQsIG5hLnJtID0gVFJVRSksDQogICAgbGF0X21pbiA9IG1pbihMYXRfdXBkYXRlZCwgbmEucm0gPSBUUlVFKSwNCiAgICBsYXRfbWF4ID0gbWF4KExhdF91cGRhdGVkLCBuYS5ybSA9IFRSVUUpDQogICkNCg0KIyBBZGQgcGFkZGluZyB0byB0aGUgZXh0ZW50IChhZGp1c3QgYXMgbmVlZGVkKQ0KcGFkZGluZyA8LSAwLjUgICMgQWRqdXN0IHBhZGRpbmcgdG8geW91ciBwcmVmZXJlbmNlDQp4X2xpbWl0cyA8LSBjKHBvaW50c19leHRlbnQkbG9uX21pbiAtIHBhZGRpbmcsIHBvaW50c19leHRlbnQkbG9uX21heCArIHBhZGRpbmcpDQp5X2xpbWl0cyA8LSBjKHBvaW50c19leHRlbnQkbGF0X21pbiAtIHBhZGRpbmcsIHBvaW50c19leHRlbnQkbGF0X21heCArIHBhZGRpbmcpDQoNCiMgQ3JlYXRlIHRoZSB6b29tZWQgbWFwDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHdvcmxkLCBmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG9yID0gImdyYXkiKSArDQogIGdlb21fcG9pbnQoZGF0YSA9IGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAgICAgICAgICAgICAgZmlsdGVyKGlzLm5hKGNvdW50cnlfY29ycmVjdCkgJiBDb3VudHJ5ID09ICJEZW5tYXJrIiksDQogICAgICAgICAgICAgYWVzKHggPSBMb25fdXBkYXRlZCwgeSA9IExhdF91cGRhdGVkLCBjb2xvciA9IENvdW50cnkpLA0KICAgICAgICAgICAgIHNpemUgPSAyKSArDQogIGdlb21fdGV4dChkYXRhID0gd29ybGRfY2VudHJvaWRzLCANCiAgICAgICAgICAgIGFlcyh4ID0gbG9uLCB5ID0gbGF0LCBsYWJlbCA9IHNvdmVyZWlnbnQpLA0KICAgICAgICAgICAgc2l6ZSA9IDMsIGNvbG9yID0gImJsYWNrIikgKyAgIyBBZGp1c3Qgc2l6ZSBhbmQgY29sb3IgYXMgbmVlZGVkDQogIGNvb3JkX3NmKHhsaW0gPSB4X2xpbWl0cywgeWxpbSA9IHlfbGltaXRzKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNClBvaW50cyB3aGVyZSBjb3VudHJ5X2NvcnJlY3QgaXMgTkEgYW5kIENvdW50cnkgID09ICJJdGFseSIuDQoNCmBgYHtyfQ0KIyBDYWxjdWxhdGUgdGhlIGV4dGVudCBvZiB0aGUgcG9pbnRzDQpwb2ludHNfZXh0ZW50IDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICBmaWx0ZXIoaXMubmEoY291bnRyeV9jb3JyZWN0KSAmIENvdW50cnkgPT0gIkl0YWx5IikgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBsb25fbWluID0gbWluKExvbl91cGRhdGVkLCBuYS5ybSA9IFRSVUUpLA0KICAgIGxvbl9tYXggPSBtYXgoTG9uX3VwZGF0ZWQsIG5hLnJtID0gVFJVRSksDQogICAgbGF0X21pbiA9IG1pbihMYXRfdXBkYXRlZCwgbmEucm0gPSBUUlVFKSwNCiAgICBsYXRfbWF4ID0gbWF4KExhdF91cGRhdGVkLCBuYS5ybSA9IFRSVUUpDQogICkNCg0KIyBBZGQgcGFkZGluZyB0byB0aGUgZXh0ZW50IChhZGp1c3QgYXMgbmVlZGVkKQ0KcGFkZGluZyA8LSAwLjUgICMgQWRqdXN0IHBhZGRpbmcgdG8geW91ciBwcmVmZXJlbmNlDQp4X2xpbWl0cyA8LSBjKHBvaW50c19leHRlbnQkbG9uX21pbiAtIHBhZGRpbmcsIHBvaW50c19leHRlbnQkbG9uX21heCArIHBhZGRpbmcpDQp5X2xpbWl0cyA8LSBjKHBvaW50c19leHRlbnQkbGF0X21pbiAtIHBhZGRpbmcsIHBvaW50c19leHRlbnQkbGF0X21heCArIHBhZGRpbmcpDQoNCiMgQ3JlYXRlIHRoZSB6b29tZWQgbWFwDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHdvcmxkLCBmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG9yID0gImdyYXkiKSArDQogIGdlb21fcG9pbnQoZGF0YSA9IGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAgICAgICAgICAgICAgZmlsdGVyKGlzLm5hKGNvdW50cnlfY29ycmVjdCkgJiBDb3VudHJ5ID09ICJJdGFseSIpLA0KICAgICAgICAgICAgIGFlcyh4ID0gTG9uX3VwZGF0ZWQsIHkgPSBMYXRfdXBkYXRlZCwgY29sb3IgPSBDb3VudHJ5KSwNCiAgICAgICAgICAgICBzaXplID0gMikgKw0KICBnZW9tX3RleHQoZGF0YSA9IHdvcmxkX2NlbnRyb2lkcywgDQogICAgICAgICAgICBhZXMoeCA9IGxvbiwgeSA9IGxhdCwgbGFiZWwgPSBzb3ZlcmVpZ250KSwNCiAgICAgICAgICAgIHNpemUgPSAzLCBjb2xvciA9ICJibGFjayIpICsgICMgQWRqdXN0IHNpemUgYW5kIGNvbG9yIGFzIG5lZWRlZA0KICBjb29yZF9zZih4bGltID0geF9saW1pdHMsIHlsaW0gPSB5X2xpbWl0cykgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQpQb2ludHMgd2hlcmUgY291bnRyeV9jb3JyZWN0IGlzIE5BIGFuZCBDb3VudHJ5IGlzIG90aGVyIG9yIE5BLg0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIHRoZSBleHRlbnQgb2YgdGhlIHBvaW50cw0KcG9pbnRzX2V4dGVudCA8LSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgZmlsdGVyKGlzLm5hKGNvdW50cnlfY29ycmVjdCkgJiAoDQogICAgQ291bnRyeSA9PSAiR2VybWFueSIgfCBDb3VudHJ5ID09ICJMaXRodWFuaWEiIHwgQ291bnRyeSA9PSAiTm9yd2F5IiB8DQogICAgICBDb3VudHJ5ID09ICJQb2xhbmQiIHwgQ291bnRyeSA9PSAiU3dlZGVuIiB8IGlzLm5hKENvdW50cnkpKSkgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBsb25fbWluID0gbWluKExvbl91cGRhdGVkLCBuYS5ybSA9IFRSVUUpLA0KICAgIGxvbl9tYXggPSBtYXgoTG9uX3VwZGF0ZWQsIG5hLnJtID0gVFJVRSksDQogICAgbGF0X21pbiA9IG1pbihMYXRfdXBkYXRlZCwgbmEucm0gPSBUUlVFKSwNCiAgICBsYXRfbWF4ID0gbWF4KExhdF91cGRhdGVkLCBuYS5ybSA9IFRSVUUpDQogICkNCg0KIyBBZGQgcGFkZGluZyB0byB0aGUgZXh0ZW50IChhZGp1c3QgYXMgbmVlZGVkKQ0KcGFkZGluZyA8LSAwLjUgICMgQWRqdXN0IHBhZGRpbmcgdG8geW91ciBwcmVmZXJlbmNlDQp4X2xpbWl0cyA8LSBjKHBvaW50c19leHRlbnQkbG9uX21pbiAtIHBhZGRpbmcsIHBvaW50c19leHRlbnQkbG9uX21heCArIHBhZGRpbmcpDQp5X2xpbWl0cyA8LSBjKHBvaW50c19leHRlbnQkbGF0X21pbiAtIHBhZGRpbmcsIHBvaW50c19leHRlbnQkbGF0X21heCArIHBhZGRpbmcpDQoNCiMgQ3JlYXRlIHRoZSB6b29tZWQgbWFwDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHdvcmxkLCBmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG9yID0gImdyYXkiKSArDQogIGdlb21fcG9pbnQoZGF0YSA9IGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAgICAgICAgICAgICAgZmlsdGVyKGlzLm5hKGNvdW50cnlfY29ycmVjdCkgJiAoDQogICAgICAgICAgICAgICAgIENvdW50cnkgPT0gIkdlcm1hbnkiIHwgQ291bnRyeSA9PSAiTGl0aHVhbmlhIiB8DQogICAgICAgICAgICAgICAgICAgQ291bnRyeSA9PSAiTm9yd2F5IiB8IENvdW50cnkgPT0gIlBvbGFuZCIgfA0KICAgICAgICAgICAgICAgICAgIENvdW50cnkgPT0gIlN3ZWRlbiIgfCBpcy5uYShDb3VudHJ5KSkpLA0KICAgICAgICAgICAgIGFlcyh4ID0gTG9uX3VwZGF0ZWQsIHkgPSBMYXRfdXBkYXRlZCwgY29sb3IgPSBDb3VudHJ5KSwNCiAgICAgICAgICAgICBzaXplID0gMikgKw0KICBnZW9tX3RleHQoZGF0YSA9IHdvcmxkX2NlbnRyb2lkcywgDQogICAgICAgICAgICBhZXMoeCA9IGxvbiwgeSA9IGxhdCwgbGFiZWwgPSBzb3ZlcmVpZ250KSwNCiAgICAgICAgICAgIHNpemUgPSAzLCBjb2xvciA9ICJibGFjayIpICsgICMgQWRqdXN0IHNpemUgYW5kIGNvbG9yIGFzIG5lZWRlZA0KICBjb29yZF9zZih4bGltID0geF9saW1pdHMsIHlsaW0gPSB5X2xpbWl0cykgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQozIHBvaW50cyB3aGVyZSBjb3VudHJ5X2NvcnJlY3QgaXMgTkEgYW5kIENvdW50cnkgaXMgTGl0aHVhbmlhIGhhdmUgbm8gY29vcmRpbmF0ZXMuDQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogIGZpbHRlcihpcy5uYShjb3VudHJ5X2NvcnJlY3QpICYgQ291bnRyeSA9PSAiTGl0aHVhbmlhIikgJT4lDQogIHNlbGVjdChMb25fdXBkYXRlZCwgTGF0X3VwZGF0ZWQpDQpgYGANCg0KIyMgQWRkIGNvbHVtbiBjb3VudHJ5X25ldyBhbmQgdXBkYXRlIGNvbHVtbiBjb3VudHJ5X2NvcnJlY3QNCg0KQ29sdW1uIGNvdW50cnlfbmV3IHNob3dzIHRoZSBnZW9jb2RlZF9jb3VudHJ5LCBvciwgZm9yIHBvaW50cyB3aGVyZSBjb3VudHJ5X2NvcnJlY3QgaXMgTkEgIGFuZCBDb3VudHJ5IGlzIFVuaXRlZCBLaW5nZG9tLCBEZW5tYXJrLCBJdGFseSwgU3dlZGVuLCBHZXJtYW55LCBOb3J3YXkgYW5kIFBvbGFuZCwgaXQgc2hvd3MgdGhlIGFjdHVhbCBDb3VudHJ5LCB3aGljaCB3YXMgY29ycmVjdCBhY2NvcmRpbmcgdG8gbWFwcy4NCg0KYGBge3J9DQpkYl9yZXN1cnZfdXBkYXRlZCA8LSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgIyBBZGQgY29sdW1uIGNvdW50cnlfbmV3DQogICMgRmlycywgY29uc2lkZXIgZ2VvY29kZWRfY291bnRyeQ0KICBtdXRhdGUoY291bnRyeV9uZXcgPSBnZW9jb2RlZF9jb3VudHJ5KSAlPiUNCiAgIyBGb3IgcG9pbnRzIHdoZXJlIGNvdW50cnlfY29ycmVjdCBpcyBOQSAgYW5kIENvdW50cnkgaXMgVW5pdGVkIEtpbmdkb20sIA0KICAjIERlbm1hcmssIEl0YWx5LCBTd2VkZW4sIEdlcm1hbnksIE5vcndheSBhbmQgUG9sYW5kLCBDb3VudHJ5IGlzIGNvcnJlY3QNCiAgbXV0YXRlKGNvdW50cnlfbmV3ID0gaWZfZWxzZSgNCiAgICBpcy5uYShjb3VudHJ5X2NvcnJlY3QpICYgDQogICAgICAoQ291bnRyeSA9PSAiVW5pdGVkIEtpbmdkb20iIHwgQ291bnRyeSA9PSAiRGVubWFyayIgfCBDb3VudHJ5ID09ICJJdGFseSIgfA0KICAgICAgICAgQ291bnRyeSA9PSAiU3dlZGVuIiB8IENvdW50cnkgPT0gIkdlcm1hbnkiIHwgQ291bnRyeSA9PSAiTm9yd2F5IikgfA0KICAgICAgQ291bnRyeSA9PSAiUG9sYW5kIiwNCiAgICBDb3VudHJ5LCBjb3VudHJ5X25ldykpICU+JQ0KICAjIEZvciBwb2ludHMgd2hlcmUgY291bnRyeV9jb3JyZWN0IGlzIE5BIGFuZCBDb3VudHJ5IGlzIE5BLCANCiAgIyBjb3VudHJ5X25ldyBzaG91bGQgYmUgR2VybWFueQ0KICBtdXRhdGUoY291bnRyeV9uZXcgPSBpZl9lbHNlKGlzLm5hKGNvdW50cnlfY29ycmVjdCkgJiBpcy5uYShDb3VudHJ5KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdlcm1hbnkiLCBjb3VudHJ5X25ldykpDQpgYGANCg0KVXBkYXRlIGNvbHVtbiBjb3VudHJ5X2NvcnJlY3QsIGJhc2VkIG9uIGlmIGNvdW50cnlfbmV3IGlzIGVxdWFsIHRvIENvdW50cnkuDQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X3VwZGF0ZWQgPC0gZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogICMgVXBkYXRlIGNvbHVtbiBjb3VudHJ5X2NvcnJlY3QgKFRSVUUgaWYgY291bnRyeV9uZXcgaXMgZXF1YWwgZnJvbSBDb3VudHJ5KQ0KICBtdXRhdGUoY291bnRyeV9jb3JyZWN0ID0gQ291bnRyeSA9PSBjb3VudHJ5X25ldykNCmBgYA0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkICU+JSBjb3VudChjb3VudHJ5X2NvcnJlY3QpDQpgYGANCg0KIyMgUmVtb3ZlIHVubmVlZGVkIGNvbHVtbg0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkIDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICBzZWxlY3QoLWdlb2NvZGVkX2NvdW50cnkpDQpgYGANCg0KIyBDb3JyZWN0aW9uIElTU1VFIDgNCg0KQ29ycmVjdCBzb21lIEVVTklTIGNvZGVzIHRoYXQgYXJlIHByb2JhYmx5IHdyb25nOg0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkIDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAjIENyZWF0ZSBhIGNvbHVtbiBlZGl0X2V4cGVydF9zeXMgdG8gbWFyayByb3dzIHRvIHVwZGF0ZQ0KICAjIFNldCBlZGl0X2V4cGVydF9zeXMgdG8gRkFMU0UgaWYgYEV4cGVydCBTeXN0ZW1gIGlzIE5BDQogIG11dGF0ZSgNCiAgICBlZGl0X2V4cGVydF9zeXMgPSBpZl9lbHNlKGlzLm5hKGBFeHBlcnQgU3lzdGVtYCksRkFMU0UsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyX2RldGVjdChgRXhwZXJ0IFN5c3RlbWAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTjE2TXxUMUNUfE4xNUEiKSkpICU+JQ0KICAjIFVwZGF0ZSBgRXhwZXJ0IFN5c3RlbWBpZiBlZGl0X2V4cGVydF9zeXMgPSBUUlVFDQogIG11dGF0ZShgRXhwZXJ0IFN5c3RlbWAgPSBpZl9lbHNlKA0KICAgIGVkaXRfZXhwZXJ0X3N5cywgDQogICAgIyBBcHBseSBzdHJpbmcgcmVwbGFjZW1lbnRzIHVzaW5nIHN0cl9yZXBsYWNlX2FsbCgpDQogICAgc3RyX3JlcGxhY2VfYWxsKGBFeHBlcnQgU3lzdGVtYCwgDQogICAgICAgICAgICAgICAgICAgIGMoIk4xNk0iID0gIk4xNiIsDQogICAgICAgICAgICAgICAgICAgICAgIlQxQ1QiID0gIlQxQyIsDQogICAgICAgICAgICAgICAgICAgICAgIk4xNUEiID0gIk4xNSIpKSxgRXhwZXJ0IFN5c3RlbWApKQ0KYGBgDQoNCiMgQ29ycmVjdGlvbiBJU1NVRSAxMA0KDQpUcmFuc2xhdGUgY29kZXMgZnJvbSBMb2NhdGlvbiBtZXRob2QgdG8gd29yZHMuDQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X3VwZGF0ZWQgPC0gZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogICMgQ3JlYXRlIGEgY29sdW1uIGVkaXRfbG9jX21ldGhvZCB0byBtYXJrIHJvd3MgdG8gdXBkYXRlDQogIG11dGF0ZShlZGl0X2xvY19tZXRob2QgPSBgTG9jYXRpb24gbWV0aG9kYCAlaW4lDQogICAgICAgICAgIGMoIjA0IiwgIjAxIiwgIjA2IiwgIjAyIiwgIjA4IiwgIjA3IiwgIjA1IiwgIjIiLCAiMDMiLCAiNCIpKSAlPiUNCiAgbXV0YXRlKGBMb2NhdGlvbiBtZXRob2RgID0gaWZfZWxzZShgTG9jYXRpb24gbWV0aG9kYCA9PSAiMiIsICIwMiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYExvY2F0aW9uIG1ldGhvZGApKSAlPiUNCiAgbXV0YXRlKGBMb2NhdGlvbiBtZXRob2RgID0gaWZfZWxzZShgTG9jYXRpb24gbWV0aG9kYCA9PSAiNCIsICIwNCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYExvY2F0aW9uIG1ldGhvZGApKSAlPiUNCiAgIyBVcGRhdGUgYExvY2F0aW9uIG1ldGhvZGBpZiBlZGl0X2xvY19tZXRob2QgPSBUUlVFDQogIG11dGF0ZShgTG9jYXRpb24gbWV0aG9kYCA9IGlmX2Vsc2UoDQogICAgZWRpdF9sb2NfbWV0aG9kLA0KICAgICMgQXBwbHkgc3RyaW5nIHJlcGxhY2VtZW50cyB1c2luZyBzdHJfcmVwbGFjZV9hbGwoKQ0KICAgIHN0cl9yZXBsYWNlX2FsbChgTG9jYXRpb24gbWV0aG9kYCwgDQogICAgICAgICAgICAgICAgICAgIGMoIjAxIiA9ICJQZXJtYW5lbnRseSBtYXJrZWQgcGxvdCBpc29sYXRlZCAoaS5lLiBzb21ld2hlcmUgd2l0aGluIHRoZSBzaXRlKSIsDQogICAgICAgICAgICAgICAgICAgICAgIjAyIiA9ICJNYXJrZWQgcGxvdCBpbiBhIGdyaWQgKGkuZS4gd2l0aCByZWd1bGFybHkgc3BhY2VkIG5laWdoYm9yIHBsb3RzKSIsDQogICAgICAgICAgICAgICAgICAgICAgIjAzIiA9ICJMb2NhdGlvbiB3aXRoIGRpZmZlcmVudGlhbCBHUFMiLA0KICAgICAgICAgICAgICAgICAgICAgICIwNCIgPSAiTG9jYXRpb24gd2l0aCBHUFMiLA0KICAgICAgICAgICAgICAgICAgICAgICIwNSIgPSAiTG9jYXRpb24gZnJvbSBhY2N1cmF0ZSBtYXAiLA0KICAgICAgICAgICAgICAgICAgICAgICIwNiIgPSAiTG9jYXRpb24gZnJvbSBhIGRlc2NyaXB0aW9uIiwNCiAgICAgICAgICAgICAgICAgICAgICAiMDciID0gIk90aGVyIiwNCiAgICAgICAgICAgICAgICAgICAgICAiMDgiID0gIk1hcmtlZCBwbG90IGluIGEgdHJhbnNlY3QiKQ0KICAgICAgICAgICAgICAgICAgICApLA0KICAgIGBMb2NhdGlvbiBtZXRob2RgKSkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChkYl9yZXN1cnZfdXBkYXRlZCwgYWVzKGBMb2NhdGlvbiBtZXRob2RgKSkgKw0KICAgICAgICAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKSAvIHN1bSguLmNvdW50Li4pICogMTAwKSkgKw0KICBsYWJzKHkgPSAiUGVyY2VudGFnZSBvZiBSZVN1cnZleSBvYnNlcnZhdGlvbnMiLA0KICAgICAgIHggPSAiTG9jYXRpb24gbWV0aG9kIikgKyBjb29yZF9mbGlwKCkNCmBgYA0KDQojIENvcnJlY3Rpb24gSVNTVUUgMTENCg0KVW5pZnkgY29kZXMgZm9yIFJlU3VydmV5IHByb2plY3QgdHlwZXMuDQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X3VwZGF0ZWQgPC0gZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogICMgQ3JlYXRlIGEgY29sdW1uIGVkaXRfUlNfUFJPSlRZUCB0byBtYXJrIHJvd3MgdG8gdXBkYXRlDQogIG11dGF0ZShlZGl0X1JTX1BST0pUWVAgPSBSU19QUk9KVFlQID09ICJSZXNhbXBsaW5nIiB8DQogICAgICAgICAgIFJTX1BST0pUWVAgPT0gIlBlcm1hbmVudCAobWFuKSIpICU+JQ0KICBtdXRhdGUoDQogICAgUlNfUFJPSlRZUCA9IHN0cl9yZXBsYWNlKFJTX1BST0pUWVAsICJeUmVzYW1wbGluZyQiLCAicmVzYW1wbGluZyIpLCANCiAgICBSU19QUk9KVFlQID0gc3RyX3JlcGxhY2UoUlNfUFJPSlRZUCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJeUGVybWFuZW50IFxcKG1hblxcKSQiLCAicGVybWFuZW50IChtYW4pIikNCiAgICApDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoZGJfcmVzdXJ2X3VwZGF0ZWQsIGFlcyhSU19QUk9KVFlQLCBmaWxsPWBNYW5pcHVsYXRlICh5L24pYCkpICsNCiAgICAgICAgIGdlb21fYmFyKGFlcyh5ID0gKC4uY291bnQuLikgLyBzdW0oLi5jb3VudC4uKSAqIDEwMCkpICsNCiAgbGFicyh5ID0gIlBlcmNlbnRhZ2Ugb2YgUmVTdXJ2ZXkgb2JzZXJ2YXRpb25zIiwNCiAgICAgICB4ID0gIlJlc3VydmV5IHByb2plY3QgdHlwZSIpICsgY29vcmRfZmxpcCgpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpDQpgYGANCg0KIyBDb3JyZWN0aW9uIElTU1VFIDEzDQoNClJlY2FsY3VsYXRlIHRoZSBjb2x1bW4gcHJlY2lzaW9uX25ldyB0byBiZSAwIHdoZW4gTG9uX3ByZWMgYW5kIExhdF9wcmVjIGFyZSBOQSwgYW5kIDEgd2hlbiBMb25fcHJlYyBhbmQgTGF0X3ByZWMgYXJlIG5vdCBOQS4NCg0KYGBge3J9DQpkYl9yZXN1cnZfdXBkYXRlZCA8LSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgIyBDcmVhdGUgYSBuZXcgY29sdW1uIGVkaXRfcHJlY2lzaW9uX25ldyB0byBtYXJrIHJvd3MgdG8gdXBkYXRlDQogICMgKHRob3NlIHdoZXJlIHByZWNpc2lvbl9uZXcgaXMgTkEgYnV0IExvbl9wcmVjIGFuZCBMYXRfcHJlYyBhcmUgbm90IE5BDQogIG11dGF0ZShlZGl0X3ByZWNpc2lvbl9uZXcgPSBpcy5uYShwcmVjaXNpb25fbmV3KSAmDQogICAgICAgICAgICghaXMubmEoTG9uX3ByZWMpICYgIWlzLm5hKExhdF9wcmVjKSkpICU+JQ0KICAjIFVwZGF0ZSBwcmVjaXNpb25fbmV3DQogIG11dGF0ZShwcmVjaXNpb25fbmV3ID0gaWZlbHNlKGlzLm5hKExvbl9wcmVjKSAmIGlzLm5hKExhdF9wcmVjKSwgMCwgMSkpDQpgYGANCg0KIyBNb3JlIGNvcnJlY3Rpb25zIFRCRD8NCg0KIyBDcmVhdGUgRVVOSVMgY29sdW1ucw0KDQpDbGVhbiBpbmZvIG9uIEV4cGVydCBzeXN0ZW0gY29sdW1uIGFuZCBzZXBhcmF0ZSBpdCB3aGVuIHRoZXJlIGFyZSBzZXZlcmFsIGNvZGVzLg0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkIDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICBtdXRhdGUoDQogICAgIyBDbGVhbiAnRXhwZXJ0IFN5c3RlbScgY29sdW1uIGJ5IHJlbW92aW5nICIhIiBhbmQgcmVwbGFjaW5nICJ+IiB3aXRoIE5BDQogICAgYEV4cGVydCBTeXN0ZW1gID0gY2FzZV93aGVuKA0KICAgICAgYEV4cGVydCBTeXN0ZW1gID09ICJ+IiB+IE5BX2NoYXJhY3Rlcl8sICAjIFJlcGxhY2UgIn4iIHdpdGggTkENCiAgICAgIFRSVUUgfiBzdHJfcmVwbGFjZV9hbGwoYEV4cGVydCBTeXN0ZW1gLCAiISIsICIiKSAgIyBSZW1vdmUgIiEiDQogICAgKQ0KICApICU+JQ0KICAjIFNlcGFyYXRlIHRoZSB2YWx1ZXMgaW4gJ0V4cGVydCBTeXN0ZW0nIGludG8gbXVsdGlwbGUgY29sdW1ucw0KICBzZXBhcmF0ZSgNCiAgICBgRXhwZXJ0IFN5c3RlbWAsDQogICAgaW50byA9IGMoIkVVTklTYSIsICJFVU5JU2IiLCAiRVVOSVNjIiwgIkVVTklTZCIpLA0KICAgIHNlcCA9ICIsIiwNCiAgICBleHRyYSA9ICJkcm9wIiwgICMgRHJvcCBleHRyYSB2YWx1ZXMgaWYgdGhlcmUgYXJlIG1vcmUgdGhhbiBjb2x1bW5zDQogICAgZmlsbCA9ICJyaWdodCIsICAgIyBGaWxsIG1pc3NpbmcgdmFsdWVzIHdpdGggTkEgZm9yIGNhc2VzIHdpdGggZmV3ZXIgdmFsdWVzDQogICAgcmVtb3ZlID0gRkFMU0UgICAgIyBLZWVwIHRoZSBvcmlnaW5hbCAnRXhwZXJ0IFN5c3RlbScgY29sdW1uDQogICkNCmBgYA0KDQpDYWxjdWxhdGUgaG93IG1hbnkgZGlmZmVyZW50IEVVTklTIGNvZGVzIGhhdmUgYmVlbiBhc3NpZ25lZDoNCg0KYGBge3J9DQpkYl9yZXN1cnZfdXBkYXRlZCA8LSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgbXV0YXRlKA0KICAgICMgQ291bnQgdGhlIG51bWJlciBvZiBub24tTkEgdmFsdWVzIGFjcm9zcyB0aGUgRVVOSVMgY29sdW1ucw0KICAgIG5fRVVOSVMgPSByb3dTdW1zKCFpcy5uYShzZWxlY3QoLiwgc3RhcnRzX3dpdGgoIkVVTklTIikpKSkNCiAgKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGRiX3Jlc3Vydl91cGRhdGVkLCBhZXMobl9FVU5JUykpICsNCiAgICAgICAgIGdlb21fYmFyKGFlcyh5ID0gKC4uY291bnQuLikgLyBzdW0oLi5jb3VudC4uKSAqIDEwMCkpICsNCiAgbGFicyh5ID0gIlBlcmNlbnRhZ2Ugb2YgUmVTdXJ2ZXkgb2JzZXJ2YXRpb25zIiwNCiAgICAgICB4ID0gIk51bWJlciBvZiBkaWZmZXJudCBFVU5JUyBjb2RlcyBhc3NpZ25lZCIpICsgY29vcmRfZmxpcCgpDQpnZ3Bsb3QoZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lIGZpbHRlcihuX0VVTklTID4gMCksIGFlcyhuX0VVTklTKSkgKw0KICAgICAgICAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKSAvIHN1bSguLmNvdW50Li4pICogMTAwKSkgKw0KICBsYWJzKHkgPSAiUGVyY2VudGFnZSBvZiBSZVN1cnZleSBvYnNlcnZhdGlvbnMiLA0KICAgICAgIHggPSAiTnVtYmVyIG9mIGRpZmZlcm50IEVVTklTIGNvZGVzIGFzc2lnbmVkIikgKyBjb29yZF9mbGlwKCkNCmBgYA0KDQpBZGQgY29sdW1ucyBmb3IgdGhlIGRpZmZlcmVudCBFVU5JUyBsZXZlbHM6DQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X3VwZGF0ZWQgPC0gZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogIG11dGF0ZSgNCiAgICAjIEVVTklTYSBsZXZlbHMNCiAgICBFVU5JU2FfMSA9IHN1YnN0cihFVU5JU2EsIDEsIGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYSwgIk1BIiksIDIsIDEpKSwNCiAgICBFVU5JU2FfMiA9IGlmZWxzZSgNCiAgICAgIG5jaGFyKEVVTklTYSkgPj0gaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNhLCAiTUEiKSwgMywgMiksIA0KICAgICAgc3Vic3RyKEVVTklTYSwgMSwgaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNhLCAiTUEiKSwgMywgMikpLA0KICAgICAgTkFfY2hhcmFjdGVyXw0KICAgICksDQogICAgRVVOSVNhXzMgPSBpZmVsc2UoDQogICAgICBuY2hhcihFVU5JU2EpID49IGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYSwgIk1BIiksIDQsIDMpLCANCiAgICAgIHN1YnN0cihFVU5JU2EsIDEsIGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYSwgIk1BIiksIDQsIDMpKSwNCiAgICAgIE5BX2NoYXJhY3Rlcl8NCiAgICAgICksDQogICAgRVVOSVNhXzQgPSBpZmVsc2UoDQogICAgICBuY2hhcihFVU5JU2EpID49IGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYSwgIk1BIiksIDUsIDQpLCANCiAgICAgIHN1YnN0cihFVU5JU2EsIDEsIGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYSwgIk1BIiksIDUsIDQpKSwNCiAgICAgIE5BX2NoYXJhY3Rlcl8NCiAgICApLA0KICAgIA0KICAgICMgRVVOSVNiIGxldmVscw0KICAgIEVVTklTYl8xID0gc3Vic3RyKEVVTklTYiwgMSwgaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNiLCAiTUEiKSwgMiwgMSkpLA0KICAgIEVVTklTYl8yID0gaWZlbHNlKA0KICAgICAgbmNoYXIoRVVOSVNiKSA+PSBpZmVsc2Uoc3RyX3N0YXJ0cyhFVU5JU2IsICJNQSIpLCAzLCAyKSwgDQogICAgICBzdWJzdHIoRVVOSVNiLCAxLCBpZmVsc2Uoc3RyX3N0YXJ0cyhFVU5JU2IsICJNQSIpLCAzLCAyKSksDQogICAgICBOQV9jaGFyYWN0ZXJfDQogICAgKSwNCiAgICBFVU5JU2JfMyA9IGlmZWxzZSgNCiAgICAgIG5jaGFyKEVVTklTYikgPj0gaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNiLCAiTUEiKSwgNCwgMyksIA0KICAgICAgc3Vic3RyKEVVTklTYiwgMSwgaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNiLCAiTUEiKSwgNCwgMykpLA0KICAgICAgTkFfY2hhcmFjdGVyXw0KICAgICksDQogICAgRVVOSVNiXzQgPSBpZmVsc2UoDQogICAgICBuY2hhcihFVU5JU2IpID49IGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYiwgIk1BIiksIDUsIDQpLCANCiAgICAgIHN1YnN0cihFVU5JU2IsIDEsIGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYiwgIk1BIiksIDUsIDQpKSwNCiAgICAgIE5BX2NoYXJhY3Rlcl8NCiAgICApLA0KICAgIA0KICAgICMgRVVOSVNjIGxldmVscw0KICAgIEVVTklTY18xID0gc3Vic3RyKEVVTklTYywgMSwgaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNjLCAiTUEiKSwgMiwgMSkpLA0KICAgIEVVTklTY18yID0gaWZlbHNlKA0KICAgICAgbmNoYXIoRVVOSVNjKSA+PSBpZmVsc2Uoc3RyX3N0YXJ0cyhFVU5JU2MsICJNQSIpLCAzLCAyKSwgDQogICAgICBzdWJzdHIoRVVOSVNjLCAxLCBpZmVsc2Uoc3RyX3N0YXJ0cyhFVU5JU2MsICJNQSIpLCAzLCAyKSksDQogICAgICBOQV9jaGFyYWN0ZXJfDQogICAgKSwNCiAgICBFVU5JU2NfMyA9IGlmZWxzZSgNCiAgICAgIG5jaGFyKEVVTklTYykgPj0gaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNjLCAiTUEiKSwgNCwgMyksIA0KICAgICAgc3Vic3RyKEVVTklTYywgMSwgaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNjLCAiTUEiKSwgNCwgMykpLA0KICAgICAgTkFfY2hhcmFjdGVyXw0KICAgICksDQogICAgRVVOSVNjXzQgPSBpZmVsc2UoDQogICAgICBuY2hhcihFVU5JU2MpID49IGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYywgIk1BIiksIDUsIDQpLCANCiAgICAgIHN1YnN0cihFVU5JU2MsIDEsIGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYywgIk1BIiksIDUsIDQpKSwNCiAgICAgIE5BX2NoYXJhY3Rlcl8NCiAgICApLA0KICAgIA0KICAgICMgRVVOSVNkIGxldmVscw0KICAgIEVVTklTZF8xID0gc3Vic3RyKEVVTklTZCwgMSwgaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNjLCAiTUEiKSwgMiwgMSkpLA0KICAgIEVVTklTZF8yID0gaWZlbHNlKA0KICAgICAgbmNoYXIoRVVOSVNkKSA+PSBpZmVsc2Uoc3RyX3N0YXJ0cyhFVU5JU2QsICJNQSIpLCAzLCAyKSwgDQogICAgICBzdWJzdHIoRVVOSVNkLCAxLCBpZmVsc2Uoc3RyX3N0YXJ0cyhFVU5JU2QsICJNQSIpLCAzLCAyKSksDQogICAgICBOQV9jaGFyYWN0ZXJfDQogICAgKSwNCiAgICBFVU5JU2RfMyA9IGlmZWxzZSgNCiAgICAgIG5jaGFyKEVVTklTZCkgPj0gaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNkLCAiTUEiKSwgNCwgMyksIA0KICAgICAgc3Vic3RyKEVVTklTZCwgMSwgaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNkLCAiTUEiKSwgNCwgMykpLA0KICAgICAgTkFfY2hhcmFjdGVyXw0KICAgICksDQogICAgRVVOSVNkXzQgPSBpZmVsc2UoDQogICAgICBuY2hhcihFVU5JU2QpID49IGlmZWxzZShzdHJfc3RhcnRzKEVVTklTZCwgIk1BIiksIDUsIDQpLCANCiAgICAgIHN1YnN0cihFVU5JU2QsIDEsIGlmZWxzZShzdHJfc3RhcnRzKEVVTklTZCwgIk1BIiksIDUsIDQpKSwNCiAgICAgIE5BX2NoYXJhY3Rlcl8NCiAgICApDQogICkNCmBgYA0KDQpDcmVhdGUgbmV3IGNvbHVtbnMgd2l0aCBkZXNjcmlwdGlvbnMgZm9yIHRoZSBsZXZlbCAxIGNvZGVzOg0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkIDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICBtdXRhdGUoDQogICAgRVVOSVNhXzFfZGVzY3IgPSBjYXNlX3doZW4oDQogICAgICBFVU5JU2FfMSA9PSAiViIgfiAiVmVnZXRhdGVkIG1hbi1tYWRlIGhhYml0YXRzIiwNCiAgICAgIEVVTklTYV8xID09ICJVIiB+ICJJbmxhbmQgaGFiaXRhdHMgd2l0aCBubyBvciBsaXR0bGUgc29pbCIsDQogICAgICBFVU5JU2FfMSA9PSAiVCIgfiAiRm9yZXN0cyBhbmQgb3RoZXIgd29vZGVkIGxhbmQiLA0KICAgICAgRVVOSVNhXzEgPT0gIlMiIH4gIkhlYXRobGFuZHMsIHNjcnViIGFuZCB0dW5kcmEiLA0KICAgICAgRVVOSVNhXzEgPT0gIlIiIH4gIkdyYXNzbGFuZHMiLA0KICAgICAgRVVOSVNhXzEgPT0gIlEiIH4gIldldGxhbmRzIiwNCiAgICAgIEVVTklTYV8xID09ICJQIiB+ICJJbmxhbmQgd2F0ZXJzIiwNCiAgICAgIEVVTklTYV8xID09ICJOIiB+ICJDb2FzdGFsIGhhYml0YXRzIiwNCiAgICAgIEVVTklTYV8xID09ICJNQSIgfiAiTWFyaW5lIGhhYml0YXRzIiwNCiAgICAgIFRSVUUgfiBOQV9jaGFyYWN0ZXJfDQogICAgKSwNCiAgICBFVU5JU2JfMV9kZXNjciA9IGNhc2Vfd2hlbigNCiAgICAgIEVVTklTYl8xID09ICJWIiB+ICJWZWdldGF0ZWQgbWFuLW1hZGUgaGFiaXRhdHMiLA0KICAgICAgRVVOSVNiXzEgPT0gIlUiIH4gIklubGFuZCBoYWJpdGF0cyB3aXRoIG5vIG9yIGxpdHRsZSBzb2lsIiwNCiAgICAgIEVVTklTYl8xID09ICJUIiB+ICJGb3Jlc3RzIGFuZCBvdGhlciB3b29kZWQgbGFuZCIsDQogICAgICBFVU5JU2JfMSA9PSAiUyIgfiAiSGVhdGhsYW5kcywgc2NydWIgYW5kIHR1bmRyYSIsDQogICAgICBFVU5JU2JfMSA9PSAiUiIgfiAiR3Jhc3NsYW5kcyIsDQogICAgICBFVU5JU2JfMSA9PSAiUSIgfiAiV2V0bGFuZHMiLA0KICAgICAgRVVOSVNiXzEgPT0gIlAiIH4gIklubGFuZCB3YXRlcnMiLA0KICAgICAgRVVOSVNiXzEgPT0gIk4iIH4gIkNvYXN0YWwgaGFiaXRhdHMiLA0KICAgICAgRVVOSVNiXzEgPT0gIk1BIiB+ICJNYXJpbmUgaGFiaXRhdHMiLA0KICAgICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8NCiAgICApLA0KICAgIEVVTklTY18xX2Rlc2NyID0gY2FzZV93aGVuKA0KICAgICAgRVVOSVNjXzEgPT0gIlYiIH4gIlZlZ2V0YXRlZCBtYW4tbWFkZSBoYWJpdGF0cyIsDQogICAgICBFVU5JU2NfMSA9PSAiVSIgfiAiSW5sYW5kIGhhYml0YXRzIHdpdGggbm8gb3IgbGl0dGxlIHNvaWwiLA0KICAgICAgRVVOSVNjXzEgPT0gIlQiIH4gIkZvcmVzdHMgYW5kIG90aGVyIHdvb2RlZCBsYW5kIiwNCiAgICAgIEVVTklTY18xID09ICJTIiB+ICJIZWF0aGxhbmRzLCBzY3J1YiBhbmQgdHVuZHJhIiwNCiAgICAgIEVVTklTY18xID09ICJSIiB+ICJHcmFzc2xhbmRzIiwNCiAgICAgIEVVTklTY18xID09ICJRIiB+ICJXZXRsYW5kcyIsDQogICAgICBFVU5JU2NfMSA9PSAiUCIgfiAiSW5sYW5kIHdhdGVycyIsDQogICAgICBFVU5JU2NfMSA9PSAiTiIgfiAiQ29hc3RhbCBoYWJpdGF0cyIsDQogICAgICBFVU5JU2NfMSA9PSAiTUEiIH4gIk1hcmluZSBoYWJpdGF0cyIsDQogICAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXw0KICAgICksDQogICAgRVVOSVNkXzFfZGVzY3IgPSBjYXNlX3doZW4oDQogICAgICBFVU5JU2RfMSA9PSAiViIgfiAiVmVnZXRhdGVkIG1hbi1tYWRlIGhhYml0YXRzIiwNCiAgICAgIEVVTklTZF8xID09ICJVIiB+ICJJbmxhbmQgaGFiaXRhdHMgd2l0aCBubyBvciBsaXR0bGUgc29pbCIsDQogICAgICBFVU5JU2RfMSA9PSAiVCIgfiAiRm9yZXN0cyBhbmQgb3RoZXIgd29vZGVkIGxhbmQiLA0KICAgICAgRVVOSVNkXzEgPT0gIlMiIH4gIkhlYXRobGFuZHMsIHNjcnViIGFuZCB0dW5kcmEiLA0KICAgICAgRVVOSVNkXzEgPT0gIlIiIH4gIkdyYXNzbGFuZHMiLA0KICAgICAgRVVOSVNkXzEgPT0gIlEiIH4gIldldGxhbmRzIiwNCiAgICAgIEVVTklTZF8xID09ICJQIiB+ICJJbmxhbmQgd2F0ZXJzIiwNCiAgICAgIEVVTklTZF8xID09ICJOIiB+ICJDb2FzdGFsIGhhYml0YXRzIiwNCiAgICAgIEVVTklTZF8xID09ICJNQSIgfiAiTWFyaW5lIGhhYml0YXRzIiwNCiAgICAgIFRSVUUgfiBOQV9jaGFyYWN0ZXJfDQogICAgKQ0KICApDQpgYGANCg0KUGxvdCBmb3IgRVVOSVNhXzEgKHRoZSBmaXJzdCBhc3NpZ25lZCBFVU5JUyBpbiBjYXNlcyBvZiBtdWx0aXBsZSBhc3NpZ25hdGlvbnMsIGxldmVsIDEpOg0KDQpgYGB7cn0NCmdncGxvdChkYl9yZXN1cnZfdXBkYXRlZCwgYWVzKEVVTklTYV8xX2Rlc2NyKSkgKw0KICBnZW9tX2JhcihhZXMoeSA9ICguLmNvdW50Li4pIC8gc3VtKC4uY291bnQuLikgKiAxMDApKSArDQogIGxhYnMoeSA9ICJQZXJjZW50YWdlIG9mIFJlU3VydmV5IG9ic2VydmF0aW9ucyIsIHggPSAiRVVOSVMgbGV2ZWwgMSIpICsNCiAgY29vcmRfZmxpcCgpDQpnZ3Bsb3QoZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lIGZpbHRlcighaXMubmEoRVVOSVNhXzFfZGVzY3IpKSwgDQogICAgICAgYWVzKEVVTklTYV8xX2Rlc2NyKSkgKw0KICBnZW9tX2JhcihhZXMoeSA9ICguLmNvdW50Li4pIC8gc3VtKC4uY291bnQuLikgKiAxMDApKSArDQogIGxhYnMoeSA9ICJQZXJjZW50YWdlIG9mIFJlU3VydmV5IG9ic2VydmF0aW9ucyIsIHggPSAiRVVOSVMgbGV2ZWwgMSIpICsgDQogIGNvb3JkX2ZsaXAoKQ0KYGBgDQoNCiMgQ29ycmVjdCB0b3BvZ3JhcGhpYyB2YXJzIGFzIG51bWVyaWMNCg0KQ29ycmVjdCBzb21lIHZhbHVlcyBhbmQgc2V0IGFsdGl0dWRlLCBzbG9wZSBhbmQgYXNwZWN0IGFzIG51bWVyaWM6DQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X3VwZGF0ZWQgPC0gZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogICAjIENyZWF0ZSBhIG5ldyBjb2x1bW4gZWRpdF9hbHRpdHVkZSB0byBtYXJrIHJvd3MgdG8gdXBkYXRlDQogICMgU2V0IGVkaXRfYXRpdHVkZSB0byBGQUxTRSBpZiBBbHRpdHVkZSBpcyBOQQ0KICBtdXRhdGUoZWRpdF9hdGl0dWRlID0gaWZfZWxzZShpcy5uYShBbHRpdHVkZSksIEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJfZGV0ZWN0KEFsdGl0dWRlLCAiLSIpKSkgJT4lDQogICMgVXBkYXRlIEFsdGl0dWRlIGlmIGVkaXRfYWx0aXR1ZGUgPSBUUlVFDQogIG11dGF0ZSggQWx0aXR1ZGUgPSBpZl9lbHNlKGVkaXRfYXRpdHVkZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgU29tZSBhbHRpdHVkZSB2YWx1ZXMgaGF2ZSBhICItIiBhZnRlciB0aGUgbnVtYmVyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGNvbnZlcnQgdG8gbnVtZXJpYyBhZnRlciByZW1vdmluZyB0aGF0DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLm51bWVyaWMoZ3N1YigiLSIsICIiLCBBbHRpdHVkZSkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBbHRpdHVkZSkpDQpgYGANCg0KYGBge3J9DQpkYl9yZXN1cnZfdXBkYXRlZCA8LSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgIyBDcmVhdGUgYSBuZXcgY29sdW1uIGVkaXRfc2xvcGUgdG8gbWFyayByb3dzIHRvIHVwZGF0ZQ0KICAjIFNldCBlZGl0X3Nsb3BlIHRvIEZBTFNFIGlmIGBTbG9wZSAowrApYCBpcyBOQQ0KICBtdXRhdGUoZWRpdF9zbG9wZSA9IGlmX2Vsc2UoaXMubmEoYFNsb3BlICjCsClgKSwgRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJfZGV0ZWN0KGBTbG9wZSAowrApYCwiX3wtIikpKSAlPiUNCiAgIyBVcGRhdGUgYFNsb3BlICjCsClgIGlmIGVkaXRfc2xvcGUgPSBUUlVFDQogIG11dGF0ZSgNCiAgICBgU2xvcGUgKMKwKWAgPSBpZl9lbHNlKGVkaXRfc2xvcGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShgU2xvcGUgKMKwKWAgPT0gIl8iIHwgYFNsb3BlICjCsClgID09ICItIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BLCBgU2xvcGUgKMKwKWApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBgU2xvcGUgKMKwKWApKSAlPiUNCiAgIyBDb252ZXJ0IHNsb3BlIGFuZCBhc3BlY3QgdG8gbnVtZXJpYw0KICBtdXRhdGUoYFNsb3BlICjCsClgID0gYXMubnVtZXJpYyhgU2xvcGUgKMKwKWApLA0KICAgICAgICAgYEFzcGVjdCAowrApYCA9IGFzLm51bWVyaWMoYEFzcGVjdCAowrApYCkpDQpgYGANCg0KIyBBZGQgY29sdW1ucyBkYXRlIGFuZCB5ZWFyDQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X3VwZGF0ZWQgPC0gZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogIG11dGF0ZShkYXRlID0gZG15KGBEYXRlIG9mIHJlY29yZGluZ2ApLCB5ZWFyID0geWVhcihkYXRlKSkNCmBgYA0KDQojIEVVTklTIGZyb20gaW5mbyBvbiBIYWJpdGF0SUQgZnJvbSBESyANCg0KQmFzZWQgb24gaW5mb3JtYXRpb24gZ290IGZyb20gSmVzcGVyLg0KDQpUaGlzIGlzIHNvIGZhciBub3QgZmxhZ2dlZCBhcyBhIGNoYW5nZSB3aXRoIGFuICJlZGl0XyIgY29sdW1uIGJlY2F1c2UgSSBkaWQgbm90IG1vZGlmeSBhbnkgb2YgdGhlIG9yaWdpbmFsIGNvbHVtbnMgb24gdGhlIGRhdGFiYXNlLiBTZWUgaWYgd2Ugc29tZWhvdyBmbGFnIHRoZSByb3dzIHdoZXJlIEVVTklTIHdhcyBvYnRhaW5lZCBmcm9tIHRoaXMgaW5mbyBsYXRlciBvbi4NCg0KIyMgUmVhZCB0aGUgZGF0YSBzZW50IGJ5IEplc3BlciBmcm9tIERLDQoNCmBgYHtyfQ0KZGJfREtfSjwtcmVhZF90c3YoaGVyZSgiZGF0YSIsICJyYXciLA0KICAgICAgICAgICAgICAgICAgICAgICAiREtfTmF0dXJkYXRhX1Jlc19oYWJpdGF0X2hhYl9jb2Rlc19KZXNwZXIiLA0KICAgICAgICAgICAgICAgICAgIkRLX05hdHVyZGF0YV9SZXNfaGFiaXRhdF9oYWJfY29kZXMudHh0IikpDQpgYGANCg0KIyMgQWRkIGluZm8gb24gSGFiaXRhdElEIHRvIGRiX3Jlc3Vydl91cGRhdGVkDQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X3VwZGF0ZWQgPC0gZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogICMgS2VlcGluZyBhbGwgb2JzIGluIGRiX3Jlc3Vydl91cGRhdGVkIGJ1dCBub3QgYWxsIGluIGRiX0RLX0oNCiAgbGVmdF9qb2luKGRiX0RLX0ogJT4lIHNlbGVjdChQbG90T2JzZXJ2YXRpb25JRCwgSGFiaXRhdElEKSkNCmBgYA0KIyMgQ2hhbmdlIHNvbWUgQW5uZXggSSBoYWJpdGF0IGNvZGVzIHRoYXQgd2VyZSB3cm9uZw0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkIDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICBtdXRhdGUoSGFiaXRhdElEID0gYXMuY2hhcmFjdGVyKEhhYml0YXRJRCkpICU+JQ0KICBtdXRhdGUoSGFiaXRhdElEID0gaWZlbHNlKEhhYml0YXRJRCA9PSAiOTk5OCIsICI5MUQwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoSGFiaXRhdElEID09ICI5OTk5IiwgIjkxRTAiLCBIYWJpdGF0SUQpKSkNCmBgYA0KDQojIyBBZGQgaW5mbyBvbiBjb3JyZXNwb25kZW5jZXMgSGFiaXRhdElEIChESywgSmVzcGVyKSAtIEVVTklTDQoNClJlYWQgY29ycmVzcG9uZGVuY2VzIGZpbGU6DQoNCmBgYHtyfQ0KY29ycmVzcG9uZGVuY2VzPC1yZWFkX2V4Y2VsKGhlcmUoImRhdGEiLCAiZWRpdGVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjb3JyZXNwb25kZW5jZV9IYWJpdGF0SURfREsueGxzeCIpKQ0KYGBgDQoNCkFkZCBpbmZvIHRvIGRiX3Jlc3Vydl91cGRhdGVkOg0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkIDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICAjIEtlZXBpbmcgYWxsIG9icyBpbiBkYl9yZXN1cnZfdXBkYXRlZCBidXQgbm90IGFsbCBpbiBkYl9ES19KDQogIGxlZnRfam9pbihjb3JyZXNwb25kZW5jZXMgJT4lIHNlbGVjdChIYWJpdGF0SUQsIEVVTklTKSkNCmBgYA0KDQpDb3JyZWN0IE5BIHZhbHVlcyBpbiBFVU5JUw0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkIDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICBtdXRhdGUoRVVOSVMgPSBpZmVsc2UoRVVOSVMgPT0gIk5BIiwgTkEsIEVVTklTKSkNCmBgYA0KDQpBZGQgaW5mbyBvbiBFVU5JUyAoREspIHRvIEVVTklTYToNCg0KYGBge3J9DQpkYl9yZXN1cnZfdXBkYXRlZCA8LSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgbXV0YXRlKEVVTklTYSA9DQogICAgICAgICAgICMgSWYgRVVOSVMgKERLKSBpcyBhdmFpbGFibGUsIGFkZCBhcyBFVU5JU2ENCiAgICAgICAgICAgaWZlbHNlKCFpcy5uYShFVU5JUyksIEVVTklTLCANCiAgICAgICAgICAgICAgICAgICMgT3RoZXJ3aXNlIGtlZXAgRVVOSVNhDQogICAgICAgICAgICAgICAgICBFVU5JU2EpLA0KICAgICAgICAgRVVOSVNfYXNzaWduYXRpb24gPSBpZmVsc2UoIWlzLm5hKEVVTklTKSwgIkluZm8gZnJvbSBESyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoaXMubmEoRVVOSVNhKSwgIk5vdCBwb3NzaWJsZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkV4cGVydCBzeXN0ZW0iKSkpICU+JQ0KICAjIFJlbW92ZSBjb2x1bW4gRVVOSVMgKERLKQ0KICBzZWxlY3QoLUVVTklTKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGRiX3Jlc3Vydl91cGRhdGVkLCBhZXMoRVVOSVNfYXNzaWduYXRpb24pKSArDQogICAgICAgICBnZW9tX2JhcihhZXMoeSA9ICguLmNvdW50Li4pIC8gc3VtKC4uY291bnQuLikgKiAxMDApKSArDQogIGxhYnMoeSA9ICJQZXJjZW50YWdlIG9mIFJlU3VydmV5IG9ic2VydmF0aW9ucyIsDQogICAgICAgeCA9ICJFVU5JUyBhc3NpZ25hdGlvbiIpDQpgYGANCg0KIyMgVXBkYXRlIGNvbHVtbnMgZm9yIEVVTklTIGxldmVscyBhbmQgZGVzY3JpcHRpb25zDQoNClVwZGF0ZSB0aGUgY29sdW1ucyBmb3IgdGhlIGRpZmZlcmVudCBFVU5JU3MgbGV2ZWxzOg0KDQpgYGB7cn0NCmRiX3Jlc3Vydl91cGRhdGVkIDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICBtdXRhdGUoDQogICAgIyBFVU5JU2EgbGV2ZWxzDQogICAgRVVOSVNhXzEgPSBzdWJzdHIoRVVOSVNhLCAxLCBpZmVsc2Uoc3RyX3N0YXJ0cyhFVU5JU2EsICJNQSIpLCAyLCAxKSksDQogICAgRVVOSVNhXzIgPSBpZmVsc2UoDQogICAgICBuY2hhcihFVU5JU2EpID49IGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYSwgIk1BIiksIDMsIDIpLCANCiAgICAgIHN1YnN0cihFVU5JU2EsIDEsIGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYSwgIk1BIiksIDMsIDIpKSwNCiAgICAgIE5BX2NoYXJhY3Rlcl8NCiAgICApLA0KICAgIEVVTklTYV8zID0gaWZlbHNlKA0KICAgICAgbmNoYXIoRVVOSVNhKSA+PSBpZmVsc2Uoc3RyX3N0YXJ0cyhFVU5JU2EsICJNQSIpLCA0LCAzKSwgDQogICAgICBzdWJzdHIoRVVOSVNhLCAxLCBpZmVsc2Uoc3RyX3N0YXJ0cyhFVU5JU2EsICJNQSIpLCA0LCAzKSksDQogICAgICBOQV9jaGFyYWN0ZXJfDQogICAgICApLA0KICAgIEVVTklTYV80ID0gaWZlbHNlKA0KICAgICAgbmNoYXIoRVVOSVNhKSA+PSBpZmVsc2Uoc3RyX3N0YXJ0cyhFVU5JU2EsICJNQSIpLCA1LCA0KSwgDQogICAgICBzdWJzdHIoRVVOSVNhLCAxLCBpZmVsc2Uoc3RyX3N0YXJ0cyhFVU5JU2EsICJNQSIpLCA1LCA0KSksDQogICAgICBOQV9jaGFyYWN0ZXJfDQogICAgKQ0KICApICU+JQ0KICAjIFJlbW92ZSBIYWJpdGF0SUQgY29sdW1uDQogIHNlbGVjdCgtSGFiaXRhdElEKQ0KYGBgDQoNClVwZGF0ZSBjb2x1bW5zIHdpdGggZGVzY3JpcHRpb25zIGZvciB0aGUgbGV2ZWwgMSBjb2RlczoNCg0KYGBge3J9DQpkYl9yZXN1cnZfdXBkYXRlZCA8LSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgbXV0YXRlKA0KICAgIEVVTklTYV8xX2Rlc2NyID0gY2FzZV93aGVuKA0KICAgICAgRVVOSVNhXzEgPT0gIlYiIH4gIlZlZ2V0YXRlZCBtYW4tbWFkZSBoYWJpdGF0cyIsDQogICAgICBFVU5JU2FfMSA9PSAiVSIgfiAiSW5sYW5kIGhhYml0YXRzIHdpdGggbm8gb3IgbGl0dGxlIHNvaWwiLA0KICAgICAgRVVOSVNhXzEgPT0gIlQiIH4gIkZvcmVzdHMgYW5kIG90aGVyIHdvb2RlZCBsYW5kIiwNCiAgICAgIEVVTklTYV8xID09ICJTIiB+ICJIZWF0aGxhbmRzLCBzY3J1YiBhbmQgdHVuZHJhIiwNCiAgICAgIEVVTklTYV8xID09ICJSIiB+ICJHcmFzc2xhbmRzIiwNCiAgICAgIEVVTklTYV8xID09ICJRIiB+ICJXZXRsYW5kcyIsDQogICAgICBFVU5JU2FfMSA9PSAiUCIgfiAiSW5sYW5kIHdhdGVycyIsDQogICAgICBFVU5JU2FfMSA9PSAiTiIgfiAiQ29hc3RhbCBoYWJpdGF0cyIsDQogICAgICBFVU5JU2FfMSA9PSAiTUEiIH4gIk1hcmluZSBoYWJpdGF0cyIsDQogICAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXw0KICAgICkNCiAgKQ0KYGBgDQoNCiMjIFVwZGF0ZSBudW1iZXIgb2YgZGlmZmVyZW50IEVVTklTIGNvZGVzDQoNClJlY2FsY3VsYXRlIGhvdyBtYW55IGRpZmZlcmVudCBFVU5JUyBjb2RlcyBoYXZlIGJlZW4gYXNzaWduZWQ6DQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X3VwZGF0ZWQgPC0gZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lDQogIG11dGF0ZSgNCiAgICAjIENvdW50IHRoZSBudW1iZXIgb2Ygbm9uLU5BIHZhbHVlcyBhY3Jvc3MgdGhlIEVVTklTIGNvbHVtbnMNCiAgICBuX0VVTklTID0gcm93U3VtcyghaXMubmEoc2VsZWN0KC4sIEVVTklTYTpFVU5JU2QpKSkNCiAgKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGRiX3Jlc3Vydl91cGRhdGVkLCBhZXMobl9FVU5JUykpICsNCiAgICAgICAgIGdlb21fYmFyKGFlcyh5ID0gKC4uY291bnQuLikgLyBzdW0oLi5jb3VudC4uKSAqIDEwMCkpICsNCiAgbGFicyh5ID0gIlBlcmNlbnRhZ2Ugb2YgUmVTdXJ2ZXkgb2JzZXJ2YXRpb25zIiwNCiAgICAgICB4ID0gIk51bWJlciBvZiBkaWZmZXJudCBFVU5JUyBjb2RlcyBhc3NpZ25lZCIpICsgY29vcmRfZmxpcCgpDQpnZ3Bsb3QoZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lIGZpbHRlcihuX0VVTklTID4gMCksIGFlcyhuX0VVTklTKSkgKw0KICAgICAgICAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKSAvIHN1bSguLmNvdW50Li4pICogMTAwKSkgKw0KICBsYWJzKHkgPSAiUGVyY2VudGFnZSBvZiBSZVN1cnZleSBvYnNlcnZhdGlvbnMiLA0KICAgICAgIHggPSAiTnVtYmVyIG9mIGRpZmZlcm50IEVVTklTIGNvZGVzIGFzc2lnbmVkIikgKyBjb29yZF9mbGlwKCkNCmBgYA0KDQpOZXcgcGxvdCBmb3IgRVVOSVNhXzEgKHRoZSBmaXJzdCBhc3NpZ25lZCBFVU5JUyBpbiBjYXNlcyBvZiBtdWx0aXBsZSBhc3NpZ25hdGlvbnMsIGxldmVsIDEpOg0KDQpgYGB7cn0NCmdncGxvdChkYl9yZXN1cnZfdXBkYXRlZCwgYWVzKEVVTklTYV8xX2Rlc2NyKSkgKw0KICAgICAgICAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKSAvIHN1bSguLmNvdW50Li4pICogMTAwKSkgKw0KICBsYWJzKHkgPSAiUGVyY2VudGFnZSBvZiBSZVN1cnZleSBvYnNlcnZhdGlvbnMiLA0KICAgICAgIHggPSAiRVVOSVMgbGV2ZWwgMSIpICsgY29vcmRfZmxpcCgpDQpnZ3Bsb3QoZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lIGZpbHRlcighaXMubmEoRVVOSVNhXzFfZGVzY3IpKSwNCiAgICAgICBhZXMoRVVOSVNhXzFfZGVzY3IpKSArDQogICAgICAgICBnZW9tX2JhcihhZXMoeSA9ICguLmNvdW50Li4pIC8gc3VtKC4uY291bnQuLikgKiAxMDApKSArDQogIGxhYnMoeSA9ICJQZXJjZW50YWdlIG9mIFJlU3VydmV5IG9ic2VydmF0aW9ucyIsDQogICAgICAgeCA9ICJFVU5JUyBsZXZlbCAxIikgKyBjb29yZF9mbGlwKCkNCmBgYA0KDQojIEFkZCBpbmZvIG9uIGRlc2NyaXB0aW9ucyBmb3IgRVVOSVMgbGV2ZWxzIDItNA0KDQpgYGB7cn0NCmRlc2NyaXB0aW9uczwtcmVhZF9leGNlbChoZXJlKCJkYXRhIiwgImVkaXRlZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRVVOSVMtSGFiaXRhdHMtMjAyMS0wNi0wMV9tb2RpZmllZC54bHN4IikpDQpgYGANCg0KYGBge3J9DQojIERlZmluZSB0aGUgY29sdW1ucyBhbmQgY29ycmVzcG9uZGluZyBkZXNjcmlwdGlvbiBjb2x1bW4gbmFtZXMNCmV1bmlzX2NvbHMgPC0gYygiRVVOSVNhXzIiLCAiRVVOSVNhXzMiLCAiRVVOSVNhXzQiLA0KICAgICAgICAgICAgICAgICJFVU5JU2JfMiIsICJFVU5JU2JfMyIsICJFVU5JU2JfNCIsIA0KICAgICAgICAgICAgICAgICJFVU5JU2NfMiIsICJFVU5JU2NfMyIsICJFVU5JU2NfNCIsDQogICAgICAgICAgICAgICAgIkVVTklTZF8yIiwgIkVVTklTZF8zIiwgIkVVTklTZF80IikNCg0KIyBDcmVhdGUgY29ycmVzcG9uZGluZyBkZXNjcmlwdGlvbiBjb2x1bW4gbmFtZXMNCmRlc2NyX2NvbF9uYW1lcyA8LSBwYXN0ZTAoZXVuaXNfY29scywgIl9kZXNjciIpDQoNCiMgVXNlIHJlZHVjZSB0byBsb29wIHRocm91Z2ggdGhlIGNvbHVtbnMgYW5kIGpvaW4gZHluYW1pY2FsbHkgYmFzZWQgb24gbGV2ZWwNCmRiX3Jlc3Vydl91cGRhdGVkIDwtIHJlZHVjZShzZXFfYWxvbmcoZXVuaXNfY29scyksIGZ1bmN0aW9uKGRhdGEsIGkpIHsNCiAgIyBFeHRyYWN0IGxldmVsIG51bWJlciBmcm9tIHRoZSBjb2x1bW4gbmFtZSAoZS5nLiwgRVVOSVNhXzIgLT4gMikNCiAgbGV2ZWwgPC0gYXMubnVtZXJpYyhnc3ViKCJcXEQiLCAiIiwgZXVuaXNfY29sc1tpXSkpDQogIA0KICAjIEZpbHRlciBkZXNjcmlwdGlvbnMgZm9yIHRoZSBjb3JyZXNwb25kaW5nIGxldmVsDQogIGRlc2NyaXB0aW9uc19sZXZlbCA8LSBkZXNjcmlwdGlvbnMgJT4lDQogICAgZmlsdGVyKGxldmVsID09IGxldmVsKSAlPiUNCiAgICBzZWxlY3QoYEVVTklTIDIwMjAgY29kZWAsIGBFVU5JUy0yMDIxIGhhYml0YXQgbmFtZWApDQogIA0KICAjIFBlcmZvcm0gdGhlIGxlZnRfam9pbiBhbmQgcmVuYW1lIHRoZSBjb2x1bW4gZHluYW1pY2FsbHkNCiAgZGF0YSAlPiUNCiAgICBsZWZ0X2pvaW4oDQogICAgICBkZXNjcmlwdGlvbnNfbGV2ZWwsDQogICAgICBieSA9IHNldE5hbWVzKCJFVU5JUyAyMDIwIGNvZGUiLCBldW5pc19jb2xzW2ldKQ0KICAgICkgJT4lDQogICAgcmVuYW1lKCEhZGVzY3JfY29sX25hbWVzW2ldIDo9IGBFVU5JUy0yMDIxIGhhYml0YXQgbmFtZWApDQp9LCAuaW5pdCA9IGRiX3Jlc3Vydl91cGRhdGVkKQ0KYGBgDQoNClRoZSBtYXRjaGluZyBkaWQgbm90IHdvcmsgc29tZXRpbWVzLCBjb3JyZWN0IQ0KDQpgYGB7cn0NCiMgQ29ycmVjdCBFVU5JU2EgbGV2ZWxzIDItNCBkZXNjcmlwdGlvbnMNCmRiX3Jlc3Vydl91cGRhdGVkIDwtIGRiX3Jlc3Vydl91cGRhdGVkICU+JQ0KICBtdXRhdGUoRVVOSVNhXzJfZGVzY3IgPSANCiAgICAgICAgICAgaWZlbHNlKCFpcy5uYShFVU5JU2FfMl9kZXNjciksIEVVTklTYV8yX2Rlc2NyLA0KICAgICAgICAgICAgICAgICAgY2FzZV93aGVuKA0KICAgICAgICAgICAgICAgICAgICBFVU5JU2FfMiA9PSAiUGYiIH4gIkZyZXNoLXdhdGVyIHN1Ym1lcmdlZCB2ZWdldGF0aW9uIiwNCiAgICAgICAgICAgICAgICAgICAgRVVOSVNhXzIgPT0gIlBqIiB+ICJTdG9uZXdvcnQgdmVnZXRhdGlvbiIsDQogICAgICAgICAgICAgICAgICAgIEVVTklTYV8yID09ICJSNCIgfiAiQWxwaW5lIGFuZCBzdWJhbHBpbmUgZ3Jhc3NsYW5kcyIsDQogICAgICAgICAgICAgICAgICAgIEVVTklTYV8yID09ICJQYiIgfiAiQ2FsY2FyZW91cyBzcHJpbmcgYW5kIHNwcmluZyBicm9vayIsDQogICAgICAgICAgICAgICAgICAgIEVVTklTYV8yID09ICJRYiIgfiAiV2V0bGFuZHMiLA0KICAgICAgICAgICAgICAgICAgICBFVU5JU2FfMiA9PSAiUjMiIH4gIlNlYXNvbmFsbHkgd2V0IGFuZCB3ZXQgZ3Jhc3NsYW5kcyIsDQogICAgICAgICAgICAgICAgICAgIEVVTklTYV8yID09ICJRYSIgfiAiTWlyZXMiLA0KICAgICAgICAgICAgICAgICAgICBFVU5JU2FfMiA9PSAiUGEiIH4gIkJhc2UtcG9vciBzcHJpbmcgYW5kIHNwcmluZyBicm9vayIsDQogICAgICAgICAgICAgICAgICAgIEVVTklTYV8yID09ICJQaCIgfiAiT2xpZ290cm9waGljLXdhdGVyIHZlZ2V0YXRpb24iLA0KICAgICAgICAgICAgICAgICAgICBFVU5JU2FfMiA9PSAiUGciIH4gIkZyZXNoLXdhdGVyIG55bXBoYWVpZCB2ZWdldGF0aW9uIiwNCiAgICAgICAgICAgICAgICAgICAgRVVOSVNhXzIgPT0NCiAgICAgICAgICAgICAgICAgICAgICAiUGQiIH4gIkZyZXNoLXdhdGVyIHNtYWxsIHBsZXVzdG9waHl0ZSB2ZWdldGF0aW9uIiwNCiAgICAgICAgICAgICAgICAgICAgRVVOSVNhXzIgPT0gIlBjIiB+ICJCcmFja2lzaC13YXRlciB2ZWdldGF0aW9uIiwNCiAgICAgICAgICAgICAgICAgICAgRVVOSVNhXzIgPT0NCiAgICAgICAgICAgICAgICAgICAgICAiUGUiIH4gIkZyZXNoLXdhdGVyIGxhcmdlIHBsZXVzdG9waHl0ZSB2ZWdldGF0aW9uIiwNCiAgICAgICAgICAgICAgICAgICAgRVVOSVNhXzIgPT0gIlBpIiB+ICJEeXN0cm9waGljLXdhdGVyIHZlZ2V0YXRpb24iLA0KICAgICAgICAgICAgICAgICAgICBFVU5JU2FfMiA9PSAiUzEiIH4gIlR1bmRyYSIsDQogICAgICAgICAgICAgICAgICAgIEVVTklTYV8yID09DQogICAgICAgICAgICAgICAgICAgICAgIlU3IiB+ICJVbnZlZ2V0YXRlZCBvciBzcGFyc2VseSB2ZWdldGF0ZWQgZ3JhdmVsIGJhcnMiLA0KICAgICAgICAgICAgICAgICAgICBFVU5JU2FfMiA9PSAiUTYiIH4gIlBlcmlvZGljYWxseSBleHBvc2VkIHNob3JlcyIsDQogICAgICAgICAgICAgICAgICAgIFRSVUUgfiBOQV9jaGFyYWN0ZXJfKQ0KICAgICAgICAgICAgICAgICAgKSwNCiAgICAgICAgIEVVTklTYV8zX2Rlc2NyID0gDQogICAgICAgICAgIGlmZWxzZSghaXMubmEoRVVOSVNhXzNfZGVzY3IpLCBFVU5JU2FfM19kZXNjciwNCiAgICAgICAgICAgICAgICAgIGNhc2Vfd2hlbigNCiAgICAgICAgICAgICAgICAgICAgRVVOSVNhXzMgPT0iVTcxIiB+ICJVbnZlZ2V0YXRlZCBvciBzcGFyc2VseSB2ZWdldGF0ZWQgZ3JhdmVsIGJhciBpbiBtb250YW5lIGFuZCBhbHBpbmUgcmVnaW9ucyIsDQogICAgICAgICAgICAgICAgICAgIEVVTklTYV8zID09IlE2MSIgfiAiUGVyaW9kaWNhbGx5IGV4cG9zZWQgc2hvcmUgd2l0aCBzdGFibGUsIGV1dHJvcGhpYyBzZWRpbWVudHMgd2l0aCBwaW9uZWVyIG9yIGVwaGVtZXJhbCB2ZWdldGF0aW9uIiwNCiAgICAgICAgICAgICAgICAgICAgRVVOSVNhXzMgPT0iUTYyIiB+ICJQZXJpb2RpY2FsbHkgZXhwb3NlZCBzaG9yZSB3aXRoIHN0YWJsZSwgbWVzb3Ryb3BoaWMgc2VkaW1lbnRzIHdpdGggcGlvbmVlciBvciBlcGhlbWVyYWwgdmVnZXRhdGlvbiIsDQogICAgICAgICAgICAgICAgICAgIFRSVUUgfiBOQV9jaGFyYWN0ZXJfDQogICAgICAgICAgICAgICAgICAgICkpDQogICAgICAgICApDQpgYGANCg0KYGBge3J9DQojIENvcnJlY3QgRVVOSVNiIGxldmVscyAyLTQgZGVzY3JpcHRpb25zDQpkYl9yZXN1cnZfdXBkYXRlZCA8LSBkYl9yZXN1cnZfdXBkYXRlZCAlPiUNCiAgbXV0YXRlKEVVTklTYl8yX2Rlc2NyID0gDQogICAgICAgICAgIGlmZWxzZSghaXMubmEoRVVOSVNiXzJfZGVzY3IpLCBFVU5JU2JfMl9kZXNjciwNCiAgICAgICAgICAgICAgICAgIGNhc2Vfd2hlbigNCiAgICAgICAgICAgICAgICAgICAgRVVOSVNiXzIgPT0gIlBqIiB+ICJTdG9uZXdvcnQgdmVnZXRhdGlvbiIsDQogICAgICAgICAgICAgICAgICAgIEVVTklTYl8yID09ICJSNCIgfiAiQWxwaW5lIGFuZCBzdWJhbHBpbmUgZ3Jhc3NsYW5kcyIsDQogICAgICAgICAgICAgICAgICAgIEVVTklTYl8yID09ICJQZiIgfiAiRnJlc2gtd2F0ZXIgc3VibWVyZ2VkIHZlZ2V0YXRpb24iLA0KICAgICAgICAgICAgICAgICAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXykNCiAgICAgICAgICAgICAgICAgICkNCiAgICAgICAgICkNCmBgYA0KDQpFVU5JU2MgYW5kIEVVTklTZCBsZXZlbHMgMi00IGFyZSBPSy4NCiANCiMgTm90ZXMgRVVOSVMgY29kZXMgLSB0byBjaGFuZ2U/DQoNCmh0dHBzOi8vd3d3LnNjaS5tdW5pLmN6L2JvdGFueS9jaHl0cnkvU2NoYW1pbmVlX2V0YWwyMDIxX0VFQS1SZXBvcnQtQXF1YXRpYy1XZXRsYW5kLWhhYml0YXRzLnBkZg0KDQpFVU5JU2FfMiA9PSAiUTYiIDogIlBlcmlvZGljYWxseSBleHBvc2VkIHNob3JlcyINCkVVTklTYV8zID0gIlE2MSIgOiAiUGVyaW9kaWNhbGx5IGV4cG9zZWQgc2hvcmUgd2l0aCBzdGFibGUsIGV1dHJvcGhpYyBzZWRpbWVudHMgd2l0aA0KcGlvbmVlciBvciBlcGhlbWVyYWwgdmVnZXRhdGlvbiINCkVVTklTYV8zID09ICJRNjIiIDogIlBlcmlvZGljYWxseSBleHBvc2VkIHNob3JlIHdpdGggc3RhYmxlLCBtZXNvdHJvcGhpYyBzZWRpbWVudHMgd2l0aCBwaW9uZWVyIG9yIGVwaGVtZXJhbCB2ZWdldGF0aW9uIg0KDQpUaGlzIGNsYXNzaWZpY2F0aW9uIG9mIFEgKyBudW1iZXJzIGlzIG5vdyBjb2V4aXN0aW5nIGluIHRoZSBkYXRhYmFzZSB3aXRoIFFhICYgUWIgKG1ldGFkYXRhKS4gSG93IHRvIHByb2NlZWQ/DQoNCmBgYHtyfQ0KZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lIGZpbHRlcihFVU5JU2FfMSA9PSAiUSIpICU+JSBkaXN0aW5jdChFVU5JU2FfMikNCmBgYA0KDQoNCiMgUGxvdHMgb2YgbGV2ZWwtMiBjYXRlZ29yaWVzIHdpdGhpbiBlYWNoIGxldmVsIDEgY2F0ZWdvcnkNCg0KYGBge3J9DQpnZ3Bsb3QoZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lIGZpbHRlcihFVU5JU2FfMSA9PSAiTUEiKSwgYWVzKEVVTklTYV8yX2Rlc2NyKSkgKw0KICAgICAgICAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKSAvIHN1bSguLmNvdW50Li4pICogMTAwKSkgKw0KICBsYWJzKHkgPSAiUGVyY2VudGFnZSBvZiBSZVN1cnZleSBvYnNlcnZhdGlvbnMiLA0KICAgICAgIHggPSAiRVVOSVMgbGV2ZWwgMiIpICsgY29vcmRfZmxpcCgpICsNCiAgZ2d0aXRsZSgNCiAgICBkYl9yZXN1cnZfdXBkYXRlZCAlPiUgZmlsdGVyKEVVTklTYV8xID09ICJNQSIpICU+JSBkaXN0aW5jdChFVU5JU2FfMV9kZXNjcikpDQpnZ3NhdmUoZmlsZW5hbWU9aGVyZSgib3V0cHV0IiwgImZpZ3VyZXMiLCJNQV9sZXZlbDIudGlmZiIpLA0KICAgICAgIHdpZHRoPTE0LGhlaWdodD04LHVuaXRzPSJjbSIsZHBpPTMwMCkNCmdncGxvdChkYl9yZXN1cnZfdXBkYXRlZCAlPiUgZmlsdGVyKEVVTklTYV8xID09ICJOIiksIGFlcyhFVU5JU2FfMl9kZXNjcikpICsNCiAgICAgICAgIGdlb21fYmFyKGFlcyh5ID0gKC4uY291bnQuLikgLyBzdW0oLi5jb3VudC4uKSAqIDEwMCkpICsNCiAgbGFicyh5ID0gIlBlcmNlbnRhZ2Ugb2YgUmVTdXJ2ZXkgb2JzZXJ2YXRpb25zIiwNCiAgICAgICB4ID0gIkVVTklTIGxldmVsIDIiKSArIGNvb3JkX2ZsaXAoKSArDQogIGdndGl0bGUoDQogICAgZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lIGZpbHRlcihFVU5JU2FfMSA9PSAiTiIpICU+JSBkaXN0aW5jdChFVU5JU2FfMV9kZXNjcikpDQpnZ3NhdmUoZmlsZW5hbWU9aGVyZSgib3V0cHV0IiwgImZpZ3VyZXMiLCJOX2xldmVsMi50aWZmIiksDQogICAgICAgd2lkdGg9MTQsaGVpZ2h0PTgsdW5pdHM9ImNtIixkcGk9MzAwKQ0KZ2dwbG90KGRiX3Jlc3Vydl91cGRhdGVkICU+JSBmaWx0ZXIoRVVOSVNhXzEgPT0gIlAiKSwgYWVzKEVVTklTYV8yX2Rlc2NyKSkgKw0KICAgICAgICAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKSAvIHN1bSguLmNvdW50Li4pICogMTAwKSkgKw0KICBsYWJzKHkgPSAiUGVyY2VudGFnZSBvZiBSZVN1cnZleSBvYnNlcnZhdGlvbnMiLA0KICAgICAgIHggPSAiRVVOSVMgbGV2ZWwgMiIpICsgY29vcmRfZmxpcCgpICsNCiAgZ2d0aXRsZSgNCiAgICBkYl9yZXN1cnZfdXBkYXRlZCAlPiUgZmlsdGVyKEVVTklTYV8xID09ICJQIikgJT4lIGRpc3RpbmN0KEVVTklTYV8xX2Rlc2NyKSkNCmdnc2F2ZShmaWxlbmFtZT1oZXJlKCJvdXRwdXQiLCAiZmlndXJlcyIsIlBfbGV2ZWwyLnRpZmYiKSwNCiAgICAgICB3aWR0aD0xNCxoZWlnaHQ9OCx1bml0cz0iY20iLGRwaT0zMDApDQpnZ3Bsb3QoZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lIGZpbHRlcihFVU5JU2FfMSA9PSAiUSIpLCBhZXMoRVVOSVNhXzJfZGVzY3IpKSArDQogICAgICAgICBnZW9tX2JhcihhZXMoeSA9ICguLmNvdW50Li4pIC8gc3VtKC4uY291bnQuLikgKiAxMDApKSArDQogIGxhYnMoeSA9ICJQZXJjZW50YWdlIG9mIFJlU3VydmV5IG9ic2VydmF0aW9ucyIsDQogICAgICAgeCA9ICJFVU5JUyBsZXZlbCAyIikgKyBjb29yZF9mbGlwKCkgKw0KICBnZ3RpdGxlKA0KICAgIGRiX3Jlc3Vydl91cGRhdGVkICU+JSBmaWx0ZXIoRVVOSVNhXzEgPT0gIlEiKSAlPiUgZGlzdGluY3QoRVVOSVNhXzFfZGVzY3IpKQ0KZ2dzYXZlKGZpbGVuYW1lPWhlcmUoIm91dHB1dCIsICJmaWd1cmVzIiwiUV9sZXZlbDIudGlmZiIpLA0KICAgICAgIHdpZHRoPTE0LGhlaWdodD04LHVuaXRzPSJjbSIsZHBpPTMwMCkNCmdncGxvdChkYl9yZXN1cnZfdXBkYXRlZCAlPiUgZmlsdGVyKEVVTklTYV8xID09ICJSIiksIGFlcyhFVU5JU2FfMl9kZXNjcikpICsNCiAgICAgICAgIGdlb21fYmFyKGFlcyh5ID0gKC4uY291bnQuLikgLyBzdW0oLi5jb3VudC4uKSAqIDEwMCkpICsNCiAgbGFicyh5ID0gIlBlcmNlbnRhZ2Ugb2YgUmVTdXJ2ZXkgb2JzZXJ2YXRpb25zIiwNCiAgICAgICB4ID0gIkVVTklTIGxldmVsIDIiKSArIGNvb3JkX2ZsaXAoKSArDQogIGdndGl0bGUoDQogICAgZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lIGZpbHRlcihFVU5JU2FfMSA9PSAiUiIpICU+JSBkaXN0aW5jdChFVU5JU2FfMV9kZXNjcikpDQpnZ3NhdmUoZmlsZW5hbWU9aGVyZSgib3V0cHV0IiwgImZpZ3VyZXMiLCJSX2xldmVsMi50aWZmIiksDQogICAgICAgd2lkdGg9MTQsaGVpZ2h0PTgsdW5pdHM9ImNtIixkcGk9MzAwKQ0KZ2dwbG90KGRiX3Jlc3Vydl91cGRhdGVkICU+JSBmaWx0ZXIoRVVOSVNhXzEgPT0gIlMiKSwgYWVzKEVVTklTYV8yX2Rlc2NyKSkgKw0KICAgICAgICAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKSAvIHN1bSguLmNvdW50Li4pICogMTAwKSkgKw0KICBsYWJzKHkgPSAiUGVyY2VudGFnZSBvZiBSZVN1cnZleSBvYnNlcnZhdGlvbnMiLA0KICAgICAgIHggPSAiRVVOSVMgbGV2ZWwgMiIpICsgY29vcmRfZmxpcCgpICsNCiAgZ2d0aXRsZSgNCiAgICBkYl9yZXN1cnZfdXBkYXRlZCAlPiUgZmlsdGVyKEVVTklTYV8xID09ICJTIikgJT4lIGRpc3RpbmN0KEVVTklTYV8xX2Rlc2NyKSkNCmdnc2F2ZShmaWxlbmFtZT1oZXJlKCJvdXRwdXQiLCAiZmlndXJlcyIsIlNfbGV2ZWwyLnRpZmYiKSwNCiAgICAgICB3aWR0aD0xNixoZWlnaHQ9OCx1bml0cz0iY20iLGRwaT0zMDApDQpnZ3Bsb3QoZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lIGZpbHRlcihFVU5JU2FfMSA9PSAiVCIpLCBhZXMoRVVOSVNhXzJfZGVzY3IpKSArDQogICAgICAgICBnZW9tX2JhcihhZXMoeSA9ICguLmNvdW50Li4pIC8gc3VtKC4uY291bnQuLikgKiAxMDApKSArDQogIGxhYnMoeSA9ICJQZXJjZW50YWdlIG9mIFJlU3VydmV5IG9ic2VydmF0aW9ucyIsDQogICAgICAgeCA9ICJFVU5JUyBsZXZlbCAyIikgKyBjb29yZF9mbGlwKCkgKw0KICBnZ3RpdGxlKA0KICAgIGRiX3Jlc3Vydl91cGRhdGVkICU+JSBmaWx0ZXIoRVVOSVNhXzEgPT0gIlQiKSAlPiUgZGlzdGluY3QoRVVOSVNhXzFfZGVzY3IpKQ0KZ2dzYXZlKGZpbGVuYW1lPWhlcmUoIm91dHB1dCIsICJmaWd1cmVzIiwiVF9sZXZlbDIudGlmZiIpLA0KICAgICAgIHdpZHRoPTE0LGhlaWdodD04LHVuaXRzPSJjbSIsZHBpPTMwMCkNCmdncGxvdChkYl9yZXN1cnZfdXBkYXRlZCAlPiUgZmlsdGVyKEVVTklTYV8xID09ICJVIiksIGFlcyhFVU5JU2FfMl9kZXNjcikpICsNCiAgICAgICAgIGdlb21fYmFyKGFlcyh5ID0gKC4uY291bnQuLikgLyBzdW0oLi5jb3VudC4uKSAqIDEwMCkpICsNCiAgbGFicyh5ID0gIlBlcmNlbnRhZ2Ugb2YgUmVTdXJ2ZXkgb2JzZXJ2YXRpb25zIiwNCiAgICAgICB4ID0gIkVVTklTIGxldmVsIDIiKSArIGNvb3JkX2ZsaXAoKSArDQogIGdndGl0bGUoDQogICAgZGJfcmVzdXJ2X3VwZGF0ZWQgJT4lIGZpbHRlcihFVU5JU2FfMSA9PSAiVSIpICU+JSBkaXN0aW5jdChFVU5JU2FfMV9kZXNjcikpDQpnZ3NhdmUoZmlsZW5hbWU9aGVyZSgib3V0cHV0IiwgImZpZ3VyZXMiLCJVX2xldmVsMi50aWZmIiksDQogICAgICAgd2lkdGg9MTYsaGVpZ2h0PTgsdW5pdHM9ImNtIixkcGk9MzAwKQ0KZ2dwbG90KGRiX3Jlc3Vydl91cGRhdGVkICU+JSBmaWx0ZXIoRVVOSVNhXzEgPT0gIlYiKSwgYWVzKEVVTklTYV8yX2Rlc2NyKSkgKw0KICAgICAgICAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKSAvIHN1bSguLmNvdW50Li4pICogMTAwKSkgKw0KICBsYWJzKHkgPSAiUGVyY2VudGFnZSBvZiBSZVN1cnZleSBvYnNlcnZhdGlvbnMiLA0KICAgICAgIHggPSAiRVVOSVMgbGV2ZWwgMiIpICsgY29vcmRfZmxpcCgpICsNCiAgZ2d0aXRsZSgNCiAgICBkYl9yZXN1cnZfdXBkYXRlZCAlPiUgZmlsdGVyKEVVTklTYV8xID09ICJWIikgJT4lIGRpc3RpbmN0KEVVTklTYV8xX2Rlc2NyKSkNCmdnc2F2ZShmaWxlbmFtZT1oZXJlKCJvdXRwdXQiLCAiZmlndXJlcyIsIlZfbGV2ZWwyLnRpZmYiKSwNCiAgICAgICB3aWR0aD0xNCxoZWlnaHQ9OCx1bml0cz0iY20iLGRwaT0zMDApDQpgYGANCg0KIyBTYXZlIHRvIGNsZWFuIGRhdGENCg0KU2F2ZSBzby1mYXIgdXBkYXRlZCBhbmQgY2xlYW4gZGF0YWZpbGUgZm9yIHJlc3VydmV5IGRhdGFiYXNlOg0KDQpgYGB7cn0NCndyaXRlX3RzdihkYl9yZXN1cnZfdXBkYXRlZCxoZXJlKCJkYXRhIiwgImNsZWFuIiwiZGJfcmVzdXJ2X3VwZGF0ZWRfY2xlYW4uY3N2IikpDQpgYGANCg0KIyBTZXNzaW9uIGluZm8NCg0KYGBge3J9DQpzZXNzaW9uSW5mbygpDQpgYGA=